Root/drivers/staging/nvec/nvec_kbd.c

1/*
2 * nvec_kbd: keyboard driver for a NVIDIA compliant embedded controller
3 *
4 * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
5 *
6 * Authors: Pierre-Hugues Husson <phhusson@free.fr>
7 * Marc Dietrich <marvin24@gmx.de>
8 *
9 * This file is subject to the terms and conditions of the GNU General Public
10 * License. See the file "COPYING" in the main directory of this archive
11 * for more details.
12 *
13 */
14
15#include <linux/module.h>
16#include <linux/slab.h>
17#include <linux/input.h>
18#include <linux/delay.h>
19#include <linux/platform_device.h>
20
21#include "nvec-keytable.h"
22#include "nvec.h"
23
24#define ACK_KBD_EVENT {'\x05', '\xed', '\x01'}
25
26static const char led_on[3] = "\x05\xed\x07";
27static const char led_off[3] = "\x05\xed\x00";
28static unsigned char keycodes[ARRAY_SIZE(code_tab_102us)
29                  + ARRAY_SIZE(extcode_tab_us102)];
30
31struct nvec_keys {
32    struct input_dev *input;
33    struct notifier_block notifier;
34    struct nvec_chip *nvec;
35    bool caps_lock;
36};
37
38static struct nvec_keys keys_dev;
39
40static void nvec_kbd_toggle_led(void)
41{
42    keys_dev.caps_lock = !keys_dev.caps_lock;
43
44    if (keys_dev.caps_lock)
45        nvec_write_async(keys_dev.nvec, led_on, sizeof(led_on));
46    else
47        nvec_write_async(keys_dev.nvec, led_off, sizeof(led_off));
48}
49
50static int nvec_keys_notifier(struct notifier_block *nb,
51                  unsigned long event_type, void *data)
52{
53    int code, state;
54    unsigned char *msg = (unsigned char *)data;
55
56    if (event_type == NVEC_KB_EVT) {
57        int _size = (msg[0] & (3 << 5)) >> 5;
58
59/* power on/off button */
60        if (_size == NVEC_VAR_SIZE)
61            return NOTIFY_STOP;
62
63        if (_size == NVEC_3BYTES)
64            msg++;
65
66        code = msg[1] & 0x7f;
67        state = msg[1] & 0x80;
68
69        if (code_tabs[_size][code] == KEY_CAPSLOCK && state)
70            nvec_kbd_toggle_led();
71
72        input_report_key(keys_dev.input, code_tabs[_size][code],
73                 !state);
74        input_sync(keys_dev.input);
75
76        return NOTIFY_STOP;
77    }
78
79    return NOTIFY_DONE;
80}
81
82static int nvec_kbd_event(struct input_dev *dev, unsigned int type,
83              unsigned int code, int value)
84{
85    unsigned char buf[] = ACK_KBD_EVENT;
86    struct nvec_chip *nvec = keys_dev.nvec;
87
88    if (type == EV_REP)
89        return 0;
90
91    if (type != EV_LED)
92        return -1;
93
94    if (code != LED_CAPSL)
95        return -1;
96
97    buf[2] = !!value;
98    nvec_write_async(nvec, buf, sizeof(buf));
99
100    return 0;
101}
102
103static int __devinit nvec_kbd_probe(struct platform_device *pdev)
104{
105    struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
106    int i, j, err;
107    struct input_dev *idev;
108
109    j = 0;
110
111    for (i = 0; i < ARRAY_SIZE(code_tab_102us); ++i)
112        keycodes[j++] = code_tab_102us[i];
113
114    for (i = 0; i < ARRAY_SIZE(extcode_tab_us102); ++i)
115        keycodes[j++] = extcode_tab_us102[i];
116
117    idev = input_allocate_device();
118    idev->name = "nvec keyboard";
119    idev->phys = "nvec";
120    idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_LED);
121    idev->ledbit[0] = BIT_MASK(LED_CAPSL);
122    idev->event = nvec_kbd_event;
123    idev->keycode = keycodes;
124    idev->keycodesize = sizeof(unsigned char);
125    idev->keycodemax = ARRAY_SIZE(keycodes);
126
127    for (i = 0; i < ARRAY_SIZE(keycodes); ++i)
128        set_bit(keycodes[i], idev->keybit);
129
130    clear_bit(0, idev->keybit);
131    err = input_register_device(idev);
132    if (err)
133        goto fail;
134
135    keys_dev.input = idev;
136    keys_dev.notifier.notifier_call = nvec_keys_notifier;
137    keys_dev.nvec = nvec;
138    nvec_register_notifier(nvec, &keys_dev.notifier, 0);
139
140    /* Enable keyboard */
141    nvec_write_async(nvec, "\x05\xf4", 2);
142
143    /* keyboard reset? */
144    nvec_write_async(nvec, "\x05\x03\x01\x01", 4);
145    nvec_write_async(nvec, "\x05\x04\x01", 3);
146    nvec_write_async(nvec, "\x06\x01\xff\x03", 4);
147/* FIXME
148    wait until keyboard reset is finished
149    or until we have a sync write */
150    mdelay(1000);
151
152    /* Disable caps lock LED */
153    nvec_write_async(nvec, led_off, sizeof(led_off));
154
155    return 0;
156
157fail:
158    input_free_device(idev);
159    return err;
160}
161
162static int __devexit nvec_kbd_remove(struct platform_device *pdev)
163{
164    input_unregister_device(keys_dev.input);
165    input_free_device(keys_dev.input);
166
167    return 0;
168}
169
170static struct platform_driver nvec_kbd_driver = {
171    .probe = nvec_kbd_probe,
172    .remove = __devexit_p(nvec_kbd_remove),
173    .driver = {
174        .name = "nvec-kbd",
175        .owner = THIS_MODULE,
176    },
177};
178
179module_platform_driver(nvec_kbd_driver);
180
181MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>");
182MODULE_DESCRIPTION("NVEC keyboard driver");
183MODULE_LICENSE("GPL");
184

Archive Download this file



interactive