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

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [misc/] [asus-laptop.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 *  asus-laptop.c - Asus Laptop Support
3
 *
4
 *
5
 *  Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
6
 *  Copyright (C) 2006-2007 Corentin Chary
7
 *
8
 *  This program is free software; you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 2 of the License, or
11
 *  (at your option) any later version.
12
 *
13
 *  This program is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
17
 *
18
 *  You should have received a copy of the GNU General Public License
19
 *  along with this program; if not, write to the Free Software
20
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
 *
22
 *
23
 *  The development page for this driver is located at
24
 *  http://sourceforge.net/projects/acpi4asus/
25
 *
26
 *  Credits:
27
 *  Pontus Fuchs   - Helper functions, cleanup
28
 *  Johann Wiesner - Small compile fixes
29
 *  John Belmonte  - ACPI code for Toshiba laptop was a good starting point.
30
 *  Eric Burghard  - LED display support for W1N
31
 *  Josh Green     - Light Sens support
32
 *  Thomas Tuttle  - His first patch for led support was very helpfull
33
 *  Sam Lin        - GPS support
34
 */
35
 
36
#include <linux/autoconf.h>
37
#include <linux/kernel.h>
38
#include <linux/module.h>
39
#include <linux/init.h>
40
#include <linux/types.h>
41
#include <linux/err.h>
42
#include <linux/proc_fs.h>
43
#include <linux/backlight.h>
44
#include <linux/fb.h>
45
#include <linux/leds.h>
46
#include <linux/platform_device.h>
47
#include <acpi/acpi_drivers.h>
48
#include <acpi/acpi_bus.h>
49
#include <asm/uaccess.h>
50
 
51
#define ASUS_LAPTOP_VERSION "0.42"
52
 
53
#define ASUS_HOTK_NAME          "Asus Laptop Support"
54
#define ASUS_HOTK_CLASS         "hotkey"
55
#define ASUS_HOTK_DEVICE_NAME   "Hotkey"
56
#define ASUS_HOTK_FILE          "asus-laptop"
57
#define ASUS_HOTK_PREFIX        "\\_SB.ATKD."
58
 
59
/*
60
 * Some events we use, same for all Asus
61
 */
62
#define ATKD_BR_UP       0x10
63
#define ATKD_BR_DOWN     0x20
64
#define ATKD_LCD_ON      0x33
65
#define ATKD_LCD_OFF     0x34
66
 
67
/*
68
 * Known bits returned by \_SB.ATKD.HWRS
69
 */
70
#define WL_HWRS     0x80
71
#define BT_HWRS     0x100
72
 
73
/*
74
 * Flags for hotk status
75
 * WL_ON and BT_ON are also used for wireless_status()
76
 */
77
#define WL_ON       0x01        //internal Wifi
78
#define BT_ON       0x02        //internal Bluetooth
79
#define MLED_ON     0x04        //mail LED
80
#define TLED_ON     0x08        //touchpad LED
81
#define RLED_ON     0x10        //Record LED
82
#define PLED_ON     0x20        //Phone LED
83
#define GLED_ON     0x40        //Gaming LED
84
#define LCD_ON      0x80        //LCD backlight
85
#define GPS_ON      0x100       //GPS
86
 
87
#define ASUS_LOG    ASUS_HOTK_FILE ": "
88
#define ASUS_ERR    KERN_ERR    ASUS_LOG
89
#define ASUS_WARNING    KERN_WARNING    ASUS_LOG
90
#define ASUS_NOTICE KERN_NOTICE ASUS_LOG
91
#define ASUS_INFO   KERN_INFO   ASUS_LOG
92
#define ASUS_DEBUG  KERN_DEBUG  ASUS_LOG
93
 
94
MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
95
MODULE_DESCRIPTION(ASUS_HOTK_NAME);
96
MODULE_LICENSE("GPL");
97
 
98
/* WAPF defines the behavior of the Fn+Fx wlan key
99
 * The significance of values is yet to be found, but
100
 * most of the time:
101
 * 0x0 will do nothing
102
 * 0x1 will allow to control the device with Fn+Fx key.
103
 * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key
104
 * 0x5 like 0x1 or 0x4
105
 * So, if something doesn't work as you want, just try other values =)
106
 */
107
static uint wapf = 1;
108
module_param(wapf, uint, 0644);
109
MODULE_PARM_DESC(wapf, "WAPF value");
110
 
111
#define ASUS_HANDLE(object, paths...)                                   \
112
        static acpi_handle  object##_handle = NULL;                     \
113
        static char *object##_paths[] = { paths }
114
 
115
/* LED */
116
ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX "MLED");
117
ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED");
118
ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED"); /* W1JC */
119
ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED"); /* A7J */
120
ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */
121
 
122
/* LEDD */
123
ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM");
124
 
125
/* Bluetooth and WLAN
126
 * WLED and BLED are not handled like other XLED, because in some dsdt
127
 * they also control the WLAN/Bluetooth device.
128
 */
129
ASUS_HANDLE(wl_switch, ASUS_HOTK_PREFIX "WLED");
130
ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED");
131
ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS");  /* All new models */
132
 
133
/* Brightness */
134
ASUS_HANDLE(brightness_set, ASUS_HOTK_PREFIX "SPLV");
135
ASUS_HANDLE(brightness_get, ASUS_HOTK_PREFIX "GPLV");
136
 
137
/* Backlight */
138
ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10",     /* All new models */
139
            "\\_SB.PCI0.ISA.EC0._Q10",  /* A1x */
140
            "\\_SB.PCI0.PX40.ECD0._Q10",        /* L3C */
141
            "\\_SB.PCI0.PX40.EC0.Q10",  /* M1A */
142
            "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */
143
            "\\_SB.PCI0.PX40.Q10",      /* S1x */
144
            "\\Q10");           /* A2x, L2D, L3D, M2E */
145
 
146
/* Display */
147
ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP");
148
ASUS_HANDLE(display_get, "\\_SB.PCI0.P0P1.VGA.GETD",    /*  A6B, A6K A6R A7D F3JM L4R M6R A3G
149
                                                           M6A M6V VX-1 V6J V6V W3Z */
150
            "\\_SB.PCI0.P0P2.VGA.GETD", /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V
151
                                           S5A M5A z33A W1Jc W2V G1 */
152
            "\\_SB.PCI0.P0P3.VGA.GETD", /* A6V A6Q */
153
            "\\_SB.PCI0.P0PA.VGA.GETD", /* A6T, A6M */
154
            "\\_SB.PCI0.PCI1.VGAC.NMAP",        /* L3C */
155
            "\\_SB.PCI0.VGA.GETD",      /* Z96F */
156
            "\\ACTD",           /* A2D */
157
            "\\ADVG",           /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
158
            "\\DNXT",           /* P30 */
159
            "\\INFB",           /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
160
            "\\SSTE");          /* A3F A6F A3N A3L M6N W3N W6A */
161
 
162
ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC");        /* Z71A Z71V */
163
ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */
164
 
165
/* GPS */
166
/* R2H use different handle for GPS on/off */
167
ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON");   /* R2H */
168
ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF");  /* R2H */
169
ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST");
170
 
171
/*
172
 * This is the main structure, we can use it to store anything interesting
173
 * about the hotk device
174
 */
175
struct asus_hotk {
176
        char *name;             //laptop name
177
        struct acpi_device *device;     //the device we are in
178
        acpi_handle handle;     //the handle of the hotk device
179
        char status;            //status of the hotk, for LEDs, ...
180
        u32 ledd_status;        //status of the LED display
181
        u8 light_level;         //light sensor level
182
        u8 light_switch;        //light sensor switch value
183
        u16 event_count[128];   //count for each event TODO make this better
184
};
185
 
186
/*
187
 * This header is made available to allow proper configuration given model,
188
 * revision number , ... this info cannot go in struct asus_hotk because it is
189
 * available before the hotk
190
 */
191
static struct acpi_table_header *asus_info;
192
 
193
/* The actual device the driver binds to */
194
static struct asus_hotk *hotk;
195
 
196
/*
197
 * The hotkey driver declaration
198
 */
199
static const struct acpi_device_id asus_device_ids[] = {
200
        {"ATK0100", 0},
201
        {"", 0},
202
};
203
MODULE_DEVICE_TABLE(acpi, asus_device_ids);
204
 
205
static int asus_hotk_add(struct acpi_device *device);
206
static int asus_hotk_remove(struct acpi_device *device, int type);
207
static struct acpi_driver asus_hotk_driver = {
208
        .name = ASUS_HOTK_NAME,
209
        .class = ASUS_HOTK_CLASS,
210
        .ids = asus_device_ids,
211
        .ops = {
212
                .add = asus_hotk_add,
213
                .remove = asus_hotk_remove,
214
                },
215
};
216
 
217
/* The backlight device /sys/class/backlight */
218
static struct backlight_device *asus_backlight_device;
219
 
220
/*
221
 * The backlight class declaration
222
 */
223
static int read_brightness(struct backlight_device *bd);
224
static int update_bl_status(struct backlight_device *bd);
225
static struct backlight_ops asusbl_ops = {
226
        .get_brightness = read_brightness,
227
        .update_status = update_bl_status,
228
};
229
 
230
/* These functions actually update the LED's, and are called from a
231
 * workqueue. By doing this as separate work rather than when the LED
232
 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
233
 * potentially bad time, such as a timer interrupt. */
234
static struct workqueue_struct *led_workqueue;
235
 
236
#define ASUS_LED(object, ledname)                                       \
237
        static void object##_led_set(struct led_classdev *led_cdev,     \
238
                                     enum led_brightness value);        \
239
        static void object##_led_update(struct work_struct *ignored);   \
240
        static int object##_led_wk;                                     \
241
        static DECLARE_WORK(object##_led_work, object##_led_update);    \
242
        static struct led_classdev object##_led = {                     \
243
                .name           = "asus:" ledname,                      \
244
                .brightness_set = object##_led_set,                     \
245
        }
246
 
247
ASUS_LED(mled, "mail");
248
ASUS_LED(tled, "touchpad");
249
ASUS_LED(rled, "record");
250
ASUS_LED(pled, "phone");
251
ASUS_LED(gled, "gaming");
252
 
253
/*
254
 * This function evaluates an ACPI method, given an int as parameter, the
255
 * method is searched within the scope of the handle, can be NULL. The output
256
 * of the method is written is output, which can also be NULL
257
 *
258
 * returns 1 if write is successful, 0 else.
259
 */
260
static int write_acpi_int(acpi_handle handle, const char *method, int val,
261
                          struct acpi_buffer *output)
262
{
263
        struct acpi_object_list params; //list of input parameters (an int here)
264
        union acpi_object in_obj;       //the only param we use
265
        acpi_status status;
266
 
267
        params.count = 1;
268
        params.pointer = &in_obj;
269
        in_obj.type = ACPI_TYPE_INTEGER;
270
        in_obj.integer.value = val;
271
 
272
        status = acpi_evaluate_object(handle, (char *)method, &params, output);
273
        return (status == AE_OK);
274
}
275
 
276
static int read_wireless_status(int mask)
277
{
278
        ulong status;
279
        acpi_status rv = AE_OK;
280
 
281
        if (!wireless_status_handle)
282
                return (hotk->status & mask) ? 1 : 0;
283
 
284
        rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
285
        if (ACPI_FAILURE(rv))
286
                printk(ASUS_WARNING "Error reading Wireless status\n");
287
        else
288
                return (status & mask) ? 1 : 0;
289
 
290
        return (hotk->status & mask) ? 1 : 0;
291
}
292
 
293
static int read_gps_status(void)
294
{
295
        ulong status;
296
        acpi_status rv = AE_OK;
297
 
298
        rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status);
299
        if (ACPI_FAILURE(rv))
300
                printk(ASUS_WARNING "Error reading GPS status\n");
301
        else
302
                return status ? 1 : 0;
303
 
304
        return (hotk->status & GPS_ON) ? 1 : 0;
305
}
306
 
307
/* Generic LED functions */
308
static int read_status(int mask)
309
{
310
        /* There is a special method for both wireless devices */
311
        if (mask == BT_ON || mask == WL_ON)
312
                return read_wireless_status(mask);
313
        else if (mask == GPS_ON)
314
                return read_gps_status();
315
 
316
        return (hotk->status & mask) ? 1 : 0;
317
}
318
 
319
static void write_status(acpi_handle handle, int out, int mask)
320
{
321
        hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask);
322
 
323
        switch (mask) {
324
        case MLED_ON:
325
                out = !out & 0x1;
326
                break;
327
        case GLED_ON:
328
                out = (out & 0x1) + 1;
329
                break;
330
        case GPS_ON:
331
                handle = (out) ? gps_on_handle : gps_off_handle;
332
                out = 0x02;
333
                break;
334
        default:
335
                out &= 0x1;
336
                break;
337
        }
338
 
339
        if (handle && !write_acpi_int(handle, NULL, out, NULL))
340
                printk(ASUS_WARNING " write failed %x\n", mask);
341
}
342
 
343
/* /sys/class/led handlers */
344
#define ASUS_LED_HANDLER(object, mask)                                  \
345
        static void object##_led_set(struct led_classdev *led_cdev,     \
346
                                     enum led_brightness value)         \
347
        {                                                               \
348
                object##_led_wk = value;                                \
349
                queue_work(led_workqueue, &object##_led_work);          \
350
        }                                                               \
351
        static void object##_led_update(struct work_struct *ignored)    \
352
        {                                                               \
353
                int value = object##_led_wk;                            \
354
                write_status(object##_set_handle, value, (mask));       \
355
        }
356
 
357
ASUS_LED_HANDLER(mled, MLED_ON);
358
ASUS_LED_HANDLER(pled, PLED_ON);
359
ASUS_LED_HANDLER(rled, RLED_ON);
360
ASUS_LED_HANDLER(tled, TLED_ON);
361
ASUS_LED_HANDLER(gled, GLED_ON);
362
 
363
static int get_lcd_state(void)
364
{
365
        return read_status(LCD_ON);
366
}
367
 
368
static int set_lcd_state(int value)
369
{
370
        int lcd = 0;
371
        acpi_status status = 0;
372
 
373
        lcd = value ? 1 : 0;
374
 
375
        if (lcd == get_lcd_state())
376
                return 0;
377
 
378
        if (lcd_switch_handle) {
379
                status = acpi_evaluate_object(lcd_switch_handle,
380
                                              NULL, NULL, NULL);
381
 
382
                if (ACPI_FAILURE(status))
383
                        printk(ASUS_WARNING "Error switching LCD\n");
384
        }
385
 
386
        write_status(NULL, lcd, LCD_ON);
387
        return 0;
388
}
389
 
390
static void lcd_blank(int blank)
391
{
392
        struct backlight_device *bd = asus_backlight_device;
393
 
394
        if (bd) {
395
                bd->props.power = blank;
396
                backlight_update_status(bd);
397
        }
398
}
399
 
400
static int read_brightness(struct backlight_device *bd)
401
{
402
        ulong value;
403
        acpi_status rv = AE_OK;
404
 
405
        rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
406
        if (ACPI_FAILURE(rv))
407
                printk(ASUS_WARNING "Error reading brightness\n");
408
 
409
        return value;
410
}
411
 
412
static int set_brightness(struct backlight_device *bd, int value)
413
{
414
        int ret = 0;
415
 
416
        value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
417
        /* 0 <= value <= 15 */
418
 
419
        if (!write_acpi_int(brightness_set_handle, NULL, value, NULL)) {
420
                printk(ASUS_WARNING "Error changing brightness\n");
421
                ret = -EIO;
422
        }
423
 
424
        return ret;
425
}
426
 
427
static int update_bl_status(struct backlight_device *bd)
428
{
429
        int rv;
430
        int value = bd->props.brightness;
431
 
432
        rv = set_brightness(bd, value);
433
        if (rv)
434
                return rv;
435
 
436
        value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0;
437
        return set_lcd_state(value);
438
}
439
 
440
/*
441
 * Platform device handlers
442
 */
443
 
444
/*
445
 * We write our info in page, we begin at offset off and cannot write more
446
 * than count bytes. We set eof to 1 if we handle those 2 values. We return the
447
 * number of bytes written in page
448
 */
449
static ssize_t show_infos(struct device *dev,
450
                          struct device_attribute *attr, char *page)
451
{
452
        int len = 0;
453
        ulong temp;
454
        char buf[16];           //enough for all info
455
        acpi_status rv = AE_OK;
456
 
457
        /*
458
         * We use the easy way, we don't care of off and count, so we don't set eof
459
         * to 1
460
         */
461
 
462
        len += sprintf(page, ASUS_HOTK_NAME " " ASUS_LAPTOP_VERSION "\n");
463
        len += sprintf(page + len, "Model reference    : %s\n", hotk->name);
464
        /*
465
         * The SFUN method probably allows the original driver to get the list
466
         * of features supported by a given model. For now, 0x0100 or 0x0800
467
         * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
468
         * The significance of others is yet to be found.
469
         */
470
        rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp);
471
        if (!ACPI_FAILURE(rv))
472
                len += sprintf(page + len, "SFUN value         : 0x%04x\n",
473
                               (uint) temp);
474
        /*
475
         * Another value for userspace: the ASYM method returns 0x02 for
476
         * battery low and 0x04 for battery critical, its readings tend to be
477
         * more accurate than those provided by _BST.
478
         * Note: since not all the laptops provide this method, errors are
479
         * silently ignored.
480
         */
481
        rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp);
482
        if (!ACPI_FAILURE(rv))
483
                len += sprintf(page + len, "ASYM value         : 0x%04x\n",
484
                               (uint) temp);
485
        if (asus_info) {
486
                snprintf(buf, 16, "%d", asus_info->length);
487
                len += sprintf(page + len, "DSDT length        : %s\n", buf);
488
                snprintf(buf, 16, "%d", asus_info->checksum);
489
                len += sprintf(page + len, "DSDT checksum      : %s\n", buf);
490
                snprintf(buf, 16, "%d", asus_info->revision);
491
                len += sprintf(page + len, "DSDT revision      : %s\n", buf);
492
                snprintf(buf, 7, "%s", asus_info->oem_id);
493
                len += sprintf(page + len, "OEM id             : %s\n", buf);
494
                snprintf(buf, 9, "%s", asus_info->oem_table_id);
495
                len += sprintf(page + len, "OEM table id       : %s\n", buf);
496
                snprintf(buf, 16, "%x", asus_info->oem_revision);
497
                len += sprintf(page + len, "OEM revision       : 0x%s\n", buf);
498
                snprintf(buf, 5, "%s", asus_info->asl_compiler_id);
499
                len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
500
                snprintf(buf, 16, "%x", asus_info->asl_compiler_revision);
501
                len += sprintf(page + len, "ASL comp revision  : 0x%s\n", buf);
502
        }
503
 
504
        return len;
505
}
506
 
507
static int parse_arg(const char *buf, unsigned long count, int *val)
508
{
509
        if (!count)
510
                return 0;
511
        if (count > 31)
512
                return -EINVAL;
513
        if (sscanf(buf, "%i", val) != 1)
514
                return -EINVAL;
515
        return count;
516
}
517
 
518
static ssize_t store_status(const char *buf, size_t count,
519
                            acpi_handle handle, int mask)
520
{
521
        int rv, value;
522
        int out = 0;
523
 
524
        rv = parse_arg(buf, count, &value);
525
        if (rv > 0)
526
                out = value ? 1 : 0;
527
 
528
        write_status(handle, out, mask);
529
 
530
        return rv;
531
}
532
 
533
/*
534
 * LEDD display
535
 */
536
static ssize_t show_ledd(struct device *dev,
537
                         struct device_attribute *attr, char *buf)
538
{
539
        return sprintf(buf, "0x%08x\n", hotk->ledd_status);
540
}
541
 
542
static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,
543
                          const char *buf, size_t count)
544
{
545
        int rv, value;
546
 
547
        rv = parse_arg(buf, count, &value);
548
        if (rv > 0) {
549
                if (!write_acpi_int(ledd_set_handle, NULL, value, NULL))
550
                        printk(ASUS_WARNING "LED display write failed\n");
551
                else
552
                        hotk->ledd_status = (u32) value;
553
        }
554
        return rv;
555
}
556
 
557
/*
558
 * WLAN
559
 */
560
static ssize_t show_wlan(struct device *dev,
561
                         struct device_attribute *attr, char *buf)
562
{
563
        return sprintf(buf, "%d\n", read_status(WL_ON));
564
}
565
 
566
static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
567
                          const char *buf, size_t count)
568
{
569
        return store_status(buf, count, wl_switch_handle, WL_ON);
570
}
571
 
572
/*
573
 * Bluetooth
574
 */
575
static ssize_t show_bluetooth(struct device *dev,
576
                              struct device_attribute *attr, char *buf)
577
{
578
        return sprintf(buf, "%d\n", read_status(BT_ON));
579
}
580
 
581
static ssize_t store_bluetooth(struct device *dev,
582
                               struct device_attribute *attr, const char *buf,
583
                               size_t count)
584
{
585
        return store_status(buf, count, bt_switch_handle, BT_ON);
586
}
587
 
588
/*
589
 * Display
590
 */
591
static void set_display(int value)
592
{
593
        /* no sanity check needed for now */
594
        if (!write_acpi_int(display_set_handle, NULL, value, NULL))
595
                printk(ASUS_WARNING "Error setting display\n");
596
        return;
597
}
598
 
599
static int read_display(void)
600
{
601
        ulong value = 0;
602
        acpi_status rv = AE_OK;
603
 
604
        /* In most of the case, we know how to set the display, but sometime
605
           we can't read it */
606
        if (display_get_handle) {
607
                rv = acpi_evaluate_integer(display_get_handle, NULL,
608
                                           NULL, &value);
609
                if (ACPI_FAILURE(rv))
610
                        printk(ASUS_WARNING "Error reading display status\n");
611
        }
612
 
613
        value &= 0x0F;          /* needed for some models, shouldn't hurt others */
614
 
615
        return value;
616
}
617
 
618
/*
619
 * Now, *this* one could be more user-friendly, but so far, no-one has
620
 * complained. The significance of bits is the same as in store_disp()
621
 */
622
static ssize_t show_disp(struct device *dev,
623
                         struct device_attribute *attr, char *buf)
624
{
625
        return sprintf(buf, "%d\n", read_display());
626
}
627
 
628
/*
629
 * Experimental support for display switching. As of now: 1 should activate
630
 * the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI.
631
 * Any combination (bitwise) of these will suffice. I never actually tested 4
632
 * displays hooked up simultaneously, so be warned. See the acpi4asus README
633
 * for more info.
634
 */
635
static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
636
                          const char *buf, size_t count)
637
{
638
        int rv, value;
639
 
640
        rv = parse_arg(buf, count, &value);
641
        if (rv > 0)
642
                set_display(value);
643
        return rv;
644
}
645
 
646
/*
647
 * Light Sens
648
 */
649
static void set_light_sens_switch(int value)
650
{
651
        if (!write_acpi_int(ls_switch_handle, NULL, value, NULL))
652
                printk(ASUS_WARNING "Error setting light sensor switch\n");
653
        hotk->light_switch = value;
654
}
655
 
656
static ssize_t show_lssw(struct device *dev,
657
                         struct device_attribute *attr, char *buf)
658
{
659
        return sprintf(buf, "%d\n", hotk->light_switch);
660
}
661
 
662
static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,
663
                          const char *buf, size_t count)
664
{
665
        int rv, value;
666
 
667
        rv = parse_arg(buf, count, &value);
668
        if (rv > 0)
669
                set_light_sens_switch(value ? 1 : 0);
670
 
671
        return rv;
672
}
673
 
674
static void set_light_sens_level(int value)
675
{
676
        if (!write_acpi_int(ls_level_handle, NULL, value, NULL))
677
                printk(ASUS_WARNING "Error setting light sensor level\n");
678
        hotk->light_level = value;
679
}
680
 
681
static ssize_t show_lslvl(struct device *dev,
682
                          struct device_attribute *attr, char *buf)
683
{
684
        return sprintf(buf, "%d\n", hotk->light_level);
685
}
686
 
687
static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,
688
                           const char *buf, size_t count)
689
{
690
        int rv, value;
691
 
692
        rv = parse_arg(buf, count, &value);
693
        if (rv > 0) {
694
                value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
695
                /* 0 <= value <= 15 */
696
                set_light_sens_level(value);
697
        }
698
 
699
        return rv;
700
}
701
 
702
/*
703
 * GPS
704
 */
705
static ssize_t show_gps(struct device *dev,
706
                        struct device_attribute *attr, char *buf)
707
{
708
        return sprintf(buf, "%d\n", read_status(GPS_ON));
709
}
710
 
711
static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
712
                         const char *buf, size_t count)
713
{
714
        return store_status(buf, count, NULL, GPS_ON);
715
}
716
 
717
static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
718
{
719
        /* TODO Find a better way to handle events count. */
720
        if (!hotk)
721
                return;
722
 
723
        /*
724
         * We need to tell the backlight device when the backlight power is
725
         * switched
726
         */
727
        if (event == ATKD_LCD_ON) {
728
                write_status(NULL, 1, LCD_ON);
729
                lcd_blank(FB_BLANK_UNBLANK);
730
        } else if (event == ATKD_LCD_OFF) {
731
                write_status(NULL, 0, LCD_ON);
732
                lcd_blank(FB_BLANK_POWERDOWN);
733
        }
734
 
735
        acpi_bus_generate_proc_event(hotk->device, event,
736
                                hotk->event_count[event % 128]++);
737
 
738
        return;
739
}
740
 
741
#define ASUS_CREATE_DEVICE_ATTR(_name)                                  \
742
        struct device_attribute dev_attr_##_name = {                    \
743
                .attr = {                                               \
744
                        .name = __stringify(_name),                     \
745
                        .mode = 0 },                                     \
746
                .show   = NULL,                                         \
747
                .store  = NULL,                                         \
748
        }
749
 
750
#define ASUS_SET_DEVICE_ATTR(_name, _mode, _show, _store)               \
751
        do {                                                            \
752
                dev_attr_##_name.attr.mode = _mode;                     \
753
                dev_attr_##_name.show = _show;                          \
754
                dev_attr_##_name.store = _store;                        \
755
        } while(0)
756
 
757
static ASUS_CREATE_DEVICE_ATTR(infos);
758
static ASUS_CREATE_DEVICE_ATTR(wlan);
759
static ASUS_CREATE_DEVICE_ATTR(bluetooth);
760
static ASUS_CREATE_DEVICE_ATTR(display);
761
static ASUS_CREATE_DEVICE_ATTR(ledd);
762
static ASUS_CREATE_DEVICE_ATTR(ls_switch);
763
static ASUS_CREATE_DEVICE_ATTR(ls_level);
764
static ASUS_CREATE_DEVICE_ATTR(gps);
765
 
766
static struct attribute *asuspf_attributes[] = {
767
        &dev_attr_infos.attr,
768
        &dev_attr_wlan.attr,
769
        &dev_attr_bluetooth.attr,
770
        &dev_attr_display.attr,
771
        &dev_attr_ledd.attr,
772
        &dev_attr_ls_switch.attr,
773
        &dev_attr_ls_level.attr,
774
        &dev_attr_gps.attr,
775
        NULL
776
};
777
 
778
static struct attribute_group asuspf_attribute_group = {
779
        .attrs = asuspf_attributes
780
};
781
 
782
static struct platform_driver asuspf_driver = {
783
        .driver = {
784
                   .name = ASUS_HOTK_FILE,
785
                   .owner = THIS_MODULE,
786
                   }
787
};
788
 
789
static struct platform_device *asuspf_device;
790
 
791
static void asus_hotk_add_fs(void)
792
{
793
        ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL);
794
 
795
        if (wl_switch_handle)
796
                ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan);
797
 
798
        if (bt_switch_handle)
799
                ASUS_SET_DEVICE_ATTR(bluetooth, 0644,
800
                                     show_bluetooth, store_bluetooth);
801
 
802
        if (display_set_handle && display_get_handle)
803
                ASUS_SET_DEVICE_ATTR(display, 0644, show_disp, store_disp);
804
        else if (display_set_handle)
805
                ASUS_SET_DEVICE_ATTR(display, 0200, NULL, store_disp);
806
 
807
        if (ledd_set_handle)
808
                ASUS_SET_DEVICE_ATTR(ledd, 0644, show_ledd, store_ledd);
809
 
810
        if (ls_switch_handle && ls_level_handle) {
811
                ASUS_SET_DEVICE_ATTR(ls_level, 0644, show_lslvl, store_lslvl);
812
                ASUS_SET_DEVICE_ATTR(ls_switch, 0644, show_lssw, store_lssw);
813
        }
814
 
815
        if (gps_status_handle && gps_on_handle && gps_off_handle)
816
                ASUS_SET_DEVICE_ATTR(gps, 0644, show_gps, store_gps);
817
}
818
 
819
static int asus_handle_init(char *name, acpi_handle * handle,
820
                            char **paths, int num_paths)
821
{
822
        int i;
823
        acpi_status status;
824
 
825
        for (i = 0; i < num_paths; i++) {
826
                status = acpi_get_handle(NULL, paths[i], handle);
827
                if (ACPI_SUCCESS(status))
828
                        return 0;
829
        }
830
 
831
        *handle = NULL;
832
        return -ENODEV;
833
}
834
 
835
#define ASUS_HANDLE_INIT(object)                                        \
836
        asus_handle_init(#object, &object##_handle, object##_paths,     \
837
                         ARRAY_SIZE(object##_paths))
838
 
839
/*
840
 * This function is used to initialize the hotk with right values. In this
841
 * method, we can make all the detection we want, and modify the hotk struct
842
 */
843
static int asus_hotk_get_info(void)
844
{
845
        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
846
        union acpi_object *model = NULL;
847
        ulong bsts_result, hwrs_result;
848
        char *string = NULL;
849
        acpi_status status;
850
 
851
        /*
852
         * Get DSDT headers early enough to allow for differentiating between
853
         * models, but late enough to allow acpi_bus_register_driver() to fail
854
         * before doing anything ACPI-specific. Should we encounter a machine,
855
         * which needs special handling (i.e. its hotkey device has a different
856
         * HID), this bit will be moved. A global variable asus_info contains
857
         * the DSDT header.
858
         */
859
        status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
860
        if (ACPI_FAILURE(status))
861
                printk(ASUS_WARNING "Couldn't get the DSDT table header\n");
862
 
863
        /* We have to write 0 on init this far for all ASUS models */
864
        if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
865
                printk(ASUS_ERR "Hotkey initialization failed\n");
866
                return -ENODEV;
867
        }
868
 
869
        /* This needs to be called for some laptops to init properly */
870
        status =
871
            acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
872
        if (ACPI_FAILURE(status))
873
                printk(ASUS_WARNING "Error calling BSTS\n");
874
        else if (bsts_result)
875
                printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n",
876
                       (uint) bsts_result);
877
 
878
        /* This too ... */
879
        write_acpi_int(hotk->handle, "CWAP", wapf, NULL);
880
 
881
        /*
882
         * Try to match the object returned by INIT to the specific model.
883
         * Handle every possible object (or the lack of thereof) the DSDT
884
         * writers might throw at us. When in trouble, we pass NULL to
885
         * asus_model_match() and try something completely different.
886
         */
887
        if (buffer.pointer) {
888
                model = buffer.pointer;
889
                switch (model->type) {
890
                case ACPI_TYPE_STRING:
891
                        string = model->string.pointer;
892
                        break;
893
                case ACPI_TYPE_BUFFER:
894
                        string = model->buffer.pointer;
895
                        break;
896
                default:
897
                        string = "";
898
                        break;
899
                }
900
        }
901
        hotk->name = kstrdup(string, GFP_KERNEL);
902
        if (!hotk->name)
903
                return -ENOMEM;
904
 
905
        if (*string)
906
                printk(ASUS_NOTICE "  %s model detected\n", string);
907
 
908
        ASUS_HANDLE_INIT(mled_set);
909
        ASUS_HANDLE_INIT(tled_set);
910
        ASUS_HANDLE_INIT(rled_set);
911
        ASUS_HANDLE_INIT(pled_set);
912
        ASUS_HANDLE_INIT(gled_set);
913
 
914
        ASUS_HANDLE_INIT(ledd_set);
915
 
916
        /*
917
         * The HWRS method return informations about the hardware.
918
         * 0x80 bit is for WLAN, 0x100 for Bluetooth.
919
         * The significance of others is yet to be found.
920
         * If we don't find the method, we assume the device are present.
921
         */
922
        status =
923
            acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result);
924
        if (ACPI_FAILURE(status))
925
                hwrs_result = WL_HWRS | BT_HWRS;
926
 
927
        if (hwrs_result & WL_HWRS)
928
                ASUS_HANDLE_INIT(wl_switch);
929
        if (hwrs_result & BT_HWRS)
930
                ASUS_HANDLE_INIT(bt_switch);
931
 
932
        ASUS_HANDLE_INIT(wireless_status);
933
 
934
        ASUS_HANDLE_INIT(brightness_set);
935
        ASUS_HANDLE_INIT(brightness_get);
936
 
937
        ASUS_HANDLE_INIT(lcd_switch);
938
 
939
        ASUS_HANDLE_INIT(display_set);
940
        ASUS_HANDLE_INIT(display_get);
941
 
942
        /* There is a lot of models with "ALSL", but a few get
943
           a real light sens, so we need to check it. */
944
        if (!ASUS_HANDLE_INIT(ls_switch))
945
                ASUS_HANDLE_INIT(ls_level);
946
 
947
        ASUS_HANDLE_INIT(gps_on);
948
        ASUS_HANDLE_INIT(gps_off);
949
        ASUS_HANDLE_INIT(gps_status);
950
 
951
        kfree(model);
952
 
953
        return AE_OK;
954
}
955
 
956
static int asus_hotk_check(void)
957
{
958
        int result = 0;
959
 
960
        result = acpi_bus_get_status(hotk->device);
961
        if (result)
962
                return result;
963
 
964
        if (hotk->device->status.present) {
965
                result = asus_hotk_get_info();
966
        } else {
967
                printk(ASUS_ERR "Hotkey device not present, aborting\n");
968
                return -EINVAL;
969
        }
970
 
971
        return result;
972
}
973
 
974
static int asus_hotk_found;
975
 
976
static int asus_hotk_add(struct acpi_device *device)
977
{
978
        acpi_status status = AE_OK;
979
        int result;
980
 
981
        if (!device)
982
                return -EINVAL;
983
 
984
        printk(ASUS_NOTICE "Asus Laptop Support version %s\n",
985
               ASUS_LAPTOP_VERSION);
986
 
987
        hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
988
        if (!hotk)
989
                return -ENOMEM;
990
 
991
        hotk->handle = device->handle;
992
        strcpy(acpi_device_name(device), ASUS_HOTK_DEVICE_NAME);
993
        strcpy(acpi_device_class(device), ASUS_HOTK_CLASS);
994
        acpi_driver_data(device) = hotk;
995
        hotk->device = device;
996
 
997
        result = asus_hotk_check();
998
        if (result)
999
                goto end;
1000
 
1001
        asus_hotk_add_fs();
1002
 
1003
        /*
1004
         * We install the handler, it will receive the hotk in parameter, so, we
1005
         * could add other data to the hotk struct
1006
         */
1007
        status = acpi_install_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
1008
                                             asus_hotk_notify, hotk);
1009
        if (ACPI_FAILURE(status))
1010
                printk(ASUS_ERR "Error installing notify handler\n");
1011
 
1012
        asus_hotk_found = 1;
1013
 
1014
        /* WLED and BLED are on by default */
1015
        write_status(bt_switch_handle, 1, BT_ON);
1016
        write_status(wl_switch_handle, 1, WL_ON);
1017
 
1018
        /* If the h/w switch is off, we need to check the real status */
1019
        write_status(NULL, read_status(BT_ON), BT_ON);
1020
        write_status(NULL, read_status(WL_ON), WL_ON);
1021
 
1022
        /* LCD Backlight is on by default */
1023
        write_status(NULL, 1, LCD_ON);
1024
 
1025
        /* LED display is off by default */
1026
        hotk->ledd_status = 0xFFF;
1027
 
1028
        /* Set initial values of light sensor and level */
1029
        hotk->light_switch = 1; /* Default to light sensor disabled */
1030
        hotk->light_level = 0;   /* level 5 for sensor sensitivity */
1031
 
1032
        if (ls_switch_handle)
1033
                set_light_sens_switch(hotk->light_switch);
1034
 
1035
        if (ls_level_handle)
1036
                set_light_sens_level(hotk->light_level);
1037
 
1038
        /* GPS is on by default */
1039
        write_status(NULL, 1, GPS_ON);
1040
 
1041
      end:
1042
        if (result) {
1043
                kfree(hotk->name);
1044
                kfree(hotk);
1045
        }
1046
 
1047
        return result;
1048
}
1049
 
1050
static int asus_hotk_remove(struct acpi_device *device, int type)
1051
{
1052
        acpi_status status = 0;
1053
 
1054
        if (!device || !acpi_driver_data(device))
1055
                return -EINVAL;
1056
 
1057
        status = acpi_remove_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
1058
                                            asus_hotk_notify);
1059
        if (ACPI_FAILURE(status))
1060
                printk(ASUS_ERR "Error removing notify handler\n");
1061
 
1062
        kfree(hotk->name);
1063
        kfree(hotk);
1064
 
1065
        return 0;
1066
}
1067
 
1068
static void asus_backlight_exit(void)
1069
{
1070
        if (asus_backlight_device)
1071
                backlight_device_unregister(asus_backlight_device);
1072
}
1073
 
1074
#define  ASUS_LED_UNREGISTER(object)                            \
1075
        if (object##_led.dev)                                   \
1076
                led_classdev_unregister(&object##_led)
1077
 
1078
static void asus_led_exit(void)
1079
{
1080
        destroy_workqueue(led_workqueue);
1081
        ASUS_LED_UNREGISTER(mled);
1082
        ASUS_LED_UNREGISTER(tled);
1083
        ASUS_LED_UNREGISTER(pled);
1084
        ASUS_LED_UNREGISTER(rled);
1085
        ASUS_LED_UNREGISTER(gled);
1086
}
1087
 
1088
static void __exit asus_laptop_exit(void)
1089
{
1090
        asus_backlight_exit();
1091
        asus_led_exit();
1092
 
1093
        acpi_bus_unregister_driver(&asus_hotk_driver);
1094
        sysfs_remove_group(&asuspf_device->dev.kobj, &asuspf_attribute_group);
1095
        platform_device_unregister(asuspf_device);
1096
        platform_driver_unregister(&asuspf_driver);
1097
}
1098
 
1099
static int asus_backlight_init(struct device *dev)
1100
{
1101
        struct backlight_device *bd;
1102
 
1103
        if (brightness_set_handle && lcd_switch_handle) {
1104
                bd = backlight_device_register(ASUS_HOTK_FILE, dev,
1105
                                               NULL, &asusbl_ops);
1106
                if (IS_ERR(bd)) {
1107
                        printk(ASUS_ERR
1108
                               "Could not register asus backlight device\n");
1109
                        asus_backlight_device = NULL;
1110
                        return PTR_ERR(bd);
1111
                }
1112
 
1113
                asus_backlight_device = bd;
1114
 
1115
                bd->props.max_brightness = 15;
1116
                bd->props.brightness = read_brightness(NULL);
1117
                bd->props.power = FB_BLANK_UNBLANK;
1118
                backlight_update_status(bd);
1119
        }
1120
        return 0;
1121
}
1122
 
1123
static int asus_led_register(acpi_handle handle,
1124
                             struct led_classdev *ldev, struct device *dev)
1125
{
1126
        if (!handle)
1127
                return 0;
1128
 
1129
        return led_classdev_register(dev, ldev);
1130
}
1131
 
1132
#define ASUS_LED_REGISTER(object, device)                               \
1133
        asus_led_register(object##_set_handle, &object##_led, device)
1134
 
1135
static int asus_led_init(struct device *dev)
1136
{
1137
        int rv;
1138
 
1139
        rv = ASUS_LED_REGISTER(mled, dev);
1140
        if (rv)
1141
                goto out;
1142
 
1143
        rv = ASUS_LED_REGISTER(tled, dev);
1144
        if (rv)
1145
                goto out1;
1146
 
1147
        rv = ASUS_LED_REGISTER(rled, dev);
1148
        if (rv)
1149
                goto out2;
1150
 
1151
        rv = ASUS_LED_REGISTER(pled, dev);
1152
        if (rv)
1153
                goto out3;
1154
 
1155
        rv = ASUS_LED_REGISTER(gled, dev);
1156
        if (rv)
1157
                goto out4;
1158
 
1159
        led_workqueue = create_singlethread_workqueue("led_workqueue");
1160
        if (!led_workqueue)
1161
                goto out5;
1162
 
1163
        return 0;
1164
out5:
1165
        rv = -ENOMEM;
1166
        ASUS_LED_UNREGISTER(gled);
1167
out4:
1168
        ASUS_LED_UNREGISTER(pled);
1169
out3:
1170
        ASUS_LED_UNREGISTER(rled);
1171
out2:
1172
        ASUS_LED_UNREGISTER(tled);
1173
out1:
1174
        ASUS_LED_UNREGISTER(mled);
1175
out:
1176
        return rv;
1177
}
1178
 
1179
static int __init asus_laptop_init(void)
1180
{
1181
        struct device *dev;
1182
        int result;
1183
 
1184
        if (acpi_disabled)
1185
                return -ENODEV;
1186
 
1187
        result = acpi_bus_register_driver(&asus_hotk_driver);
1188
        if (result < 0)
1189
                return result;
1190
 
1191
        /*
1192
         * This is a bit of a kludge.  We only want this module loaded
1193
         * for ASUS systems, but there's currently no way to probe the
1194
         * ACPI namespace for ASUS HIDs.  So we just return failure if
1195
         * we didn't find one, which will cause the module to be
1196
         * unloaded.
1197
         */
1198
        if (!asus_hotk_found) {
1199
                acpi_bus_unregister_driver(&asus_hotk_driver);
1200
                return -ENODEV;
1201
        }
1202
 
1203
        dev = acpi_get_physical_device(hotk->device->handle);
1204
 
1205
        result = asus_backlight_init(dev);
1206
        if (result)
1207
                goto fail_backlight;
1208
 
1209
        result = asus_led_init(dev);
1210
        if (result)
1211
                goto fail_led;
1212
 
1213
        /* Register platform stuff */
1214
        result = platform_driver_register(&asuspf_driver);
1215
        if (result)
1216
                goto fail_platform_driver;
1217
 
1218
        asuspf_device = platform_device_alloc(ASUS_HOTK_FILE, -1);
1219
        if (!asuspf_device) {
1220
                result = -ENOMEM;
1221
                goto fail_platform_device1;
1222
        }
1223
 
1224
        result = platform_device_add(asuspf_device);
1225
        if (result)
1226
                goto fail_platform_device2;
1227
 
1228
        result = sysfs_create_group(&asuspf_device->dev.kobj,
1229
                                    &asuspf_attribute_group);
1230
        if (result)
1231
                goto fail_sysfs;
1232
 
1233
        return 0;
1234
 
1235
      fail_sysfs:
1236
        platform_device_del(asuspf_device);
1237
 
1238
      fail_platform_device2:
1239
        platform_device_put(asuspf_device);
1240
 
1241
      fail_platform_device1:
1242
        platform_driver_unregister(&asuspf_driver);
1243
 
1244
      fail_platform_driver:
1245
        asus_led_exit();
1246
 
1247
      fail_led:
1248
        asus_backlight_exit();
1249
 
1250
      fail_backlight:
1251
 
1252
        return result;
1253
}
1254
 
1255
module_init(asus_laptop_init);
1256
module_exit(asus_laptop_exit);

powered by: WebSVN 2.1.0

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