Commit b11d2127c4893a7315d1e16273bc8560049fa3ca
Committed by
Dmitry Torokhov
1 parent
9360353f4a
Exists in
master
and in
7 other branches
Input: add support for SEGA Dreamcast keyboard
Signed-off by: Adrian McMenamin <adrian@mcmen.demon.co.uk> Acked-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Showing 3 changed files with 263 additions and 0 deletions Side-by-side Diff
drivers/input/keyboard/Kconfig
... | ... | @@ -274,5 +274,15 @@ |
274 | 274 | To compile this driver as a module, choose M here: the |
275 | 275 | module will be called gpio-keys. |
276 | 276 | |
277 | +config KEYBOARD_MAPLE | |
278 | + tristate "Maple bus keyboard" | |
279 | + depends on SH_DREAMCAST && MAPLE | |
280 | + help | |
281 | + Say Y here if you have a Dreamcast console running Linux and have | |
282 | + a keyboard attached to its Maple bus. | |
283 | + | |
284 | + To compile this driver as a module, choose M here: the | |
285 | + module will be called maple_keyb. | |
286 | + | |
277 | 287 | endif |
drivers/input/keyboard/Makefile
drivers/input/keyboard/maple_keyb.c
1 | +/* | |
2 | + * SEGA Dreamcast keyboard driver | |
3 | + * Based on drivers/usb/usbkbd.c | |
4 | + * Copyright YAEGASHI Takeshi, 2001 | |
5 | + * Porting to 2.6 Copyright Adrian McMenamin, 2007 | |
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, see the file COPYING, or write | |
19 | + * to the Free Software Foundation, Inc., | |
20 | + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
21 | + */ | |
22 | + | |
23 | +#include <linux/kernel.h> | |
24 | +#include <linux/slab.h> | |
25 | +#include <linux/input.h> | |
26 | +#include <linux/module.h> | |
27 | +#include <linux/init.h> | |
28 | +#include <linux/timer.h> | |
29 | +#include <linux/maple.h> | |
30 | +#include <asm/mach/maple.h> | |
31 | + | |
32 | +/* Very simple mutex to ensure proper cleanup */ | |
33 | +static DEFINE_MUTEX(maple_keyb_mutex); | |
34 | + | |
35 | +#define NR_SCANCODES 256 | |
36 | + | |
37 | +MODULE_AUTHOR("YAEGASHI Takeshi, Adrian McMenamin"); | |
38 | +MODULE_DESCRIPTION("SEGA Dreamcast keyboard driver"); | |
39 | +MODULE_LICENSE("GPL"); | |
40 | + | |
41 | +struct dc_kbd { | |
42 | + struct input_dev *dev; | |
43 | + unsigned short keycode[NR_SCANCODES]; | |
44 | + unsigned char new[8]; | |
45 | + unsigned char old[8]; | |
46 | +}; | |
47 | + | |
48 | +static const unsigned short dc_kbd_keycode[NR_SCANCODES] = { | |
49 | + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_A, KEY_B, KEY_C, KEY_D, | |
50 | + KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, | |
51 | + KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, | |
52 | + KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2, | |
53 | + KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, | |
54 | + KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE, | |
55 | + KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_BACKSLASH, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, | |
56 | + KEY_DOT, KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, | |
57 | + KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_SYSRQ, | |
58 | + KEY_SCROLLLOCK, KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP, KEY_DELETE, | |
59 | + KEY_END, KEY_PAGEDOWN, KEY_RIGHT, KEY_LEFT, KEY_DOWN, KEY_UP, | |
60 | + KEY_NUMLOCK, KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS, KEY_KPENTER, KEY_KP1, KEY_KP2, | |
61 | + KEY_KP3, KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7, KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT, | |
62 | + KEY_102ND, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, KEY_F13, KEY_F14, KEY_F15, | |
63 | + KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20, | |
64 | + KEY_F21, KEY_F22, KEY_F23, KEY_F24, KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT, | |
65 | + KEY_STOP, KEY_AGAIN, KEY_UNDO, KEY_CUT, KEY_COPY, KEY_PASTE, KEY_FIND, KEY_MUTE, | |
66 | + KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_KPCOMMA, KEY_RESERVED, KEY_RO, KEY_KATAKANAHIRAGANA , KEY_YEN, | |
67 | + KEY_HENKAN, KEY_MUHENKAN, KEY_KPJPCOMMA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, | |
68 | + KEY_HANGEUL, KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA, KEY_ZENKAKUHANKAKU, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, | |
69 | + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, | |
70 | + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, | |
71 | + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, | |
72 | + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, | |
73 | + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, | |
74 | + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, | |
75 | + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, | |
76 | + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, | |
77 | + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, | |
78 | + KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTMETA, KEY_RIGHTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT, KEY_RIGHTMETA, | |
79 | + KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG, KEY_NEXTSONG, KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, | |
80 | + KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, KEY_FIND, KEY_SCROLLUP, KEY_SCROLLDOWN, KEY_EDIT, KEY_SLEEP, | |
81 | + KEY_SCREENLOCK, KEY_REFRESH, KEY_CALC, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED | |
82 | +}; | |
83 | + | |
84 | +static void dc_scan_kbd(struct dc_kbd *kbd) | |
85 | +{ | |
86 | + struct input_dev *dev = kbd->dev; | |
87 | + void *ptr; | |
88 | + int code, keycode; | |
89 | + int i; | |
90 | + | |
91 | + for (i = 0; i < 8; i++) { | |
92 | + code = i + 224; | |
93 | + keycode = kbd->keycode[code]; | |
94 | + input_event(dev, EV_MSC, MSC_SCAN, code); | |
95 | + input_report_key(dev, keycode, (kbd->new[0] >> i) & 1); | |
96 | + } | |
97 | + | |
98 | + for (i = 2; i < 8; i++) { | |
99 | + ptr = memchr(kbd->new + 2, kbd->old[i], 6); | |
100 | + code = kbd->old[i]; | |
101 | + if (code > 3 && ptr == NULL) { | |
102 | + keycode = kbd->keycode[code]; | |
103 | + if (keycode) { | |
104 | + input_event(dev, EV_MSC, MSC_SCAN, code); | |
105 | + input_report_key(dev, keycode, 0); | |
106 | + } else | |
107 | + printk(KERN_DEBUG "maple_keyb: " | |
108 | + "Unknown key (scancode %#x) released.", | |
109 | + code); | |
110 | + } | |
111 | + ptr = memchr(kbd->old + 2, kbd->new[i], 6); | |
112 | + code = kbd->new[i]; | |
113 | + if (code > 3 && ptr) { | |
114 | + keycode = kbd->keycode[code]; | |
115 | + if (keycode) { | |
116 | + input_event(dev, EV_MSC, MSC_SCAN, code); | |
117 | + input_report_key(dev, keycode, 1); | |
118 | + } else | |
119 | + printk(KERN_DEBUG "maple_keyb: " | |
120 | + "Unknown key (scancode %#x) pressed.", | |
121 | + code); | |
122 | + } | |
123 | + } | |
124 | + input_sync(dev); | |
125 | + memcpy(kbd->old, kbd->new, 8); | |
126 | +} | |
127 | + | |
128 | +static void dc_kbd_callback(struct mapleq *mq) | |
129 | +{ | |
130 | + struct maple_device *mapledev = mq->dev; | |
131 | + struct dc_kbd *kbd = mapledev->private_data; | |
132 | + unsigned long *buf = mq->recvbuf; | |
133 | + | |
134 | + /* | |
135 | + * We should always be getting the lock because the only | |
136 | + * time it may be locked if driver is in cleanup phase. | |
137 | + */ | |
138 | + if (likely(mutex_trylock(&maple_keyb_mutex))) { | |
139 | + | |
140 | + if (buf[1] == mapledev->function) { | |
141 | + memcpy(kbd->new, buf + 2, 8); | |
142 | + dc_scan_kbd(kbd); | |
143 | + } | |
144 | + | |
145 | + mutex_unlock(&maple_keyb_mutex); | |
146 | + } | |
147 | +} | |
148 | + | |
149 | +static int dc_kbd_connect(struct maple_device *mdev) | |
150 | +{ | |
151 | + int i, error; | |
152 | + struct dc_kbd *kbd; | |
153 | + struct input_dev *dev; | |
154 | + | |
155 | + if (!(mdev->function & MAPLE_FUNC_KEYBOARD)) | |
156 | + return -EINVAL; | |
157 | + | |
158 | + kbd = kzalloc(sizeof(struct dc_kbd), GFP_KERNEL); | |
159 | + dev = input_allocate_device(); | |
160 | + if (!kbd || !dev) { | |
161 | + error = -ENOMEM; | |
162 | + goto fail; | |
163 | + } | |
164 | + | |
165 | + mdev->private_data = kbd; | |
166 | + | |
167 | + kbd->dev = dev; | |
168 | + memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode)); | |
169 | + | |
170 | + dev->name = mdev->product_name; | |
171 | + dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); | |
172 | + dev->keycode = kbd->keycode; | |
173 | + dev->keycodesize = sizeof (unsigned short); | |
174 | + dev->keycodemax = ARRAY_SIZE(kbd->keycode); | |
175 | + dev->id.bustype = BUS_HOST; | |
176 | + dev->dev.parent = &mdev->dev; | |
177 | + | |
178 | + for (i = 0; i < NR_SCANCODES; i++) | |
179 | + __set_bit(dc_kbd_keycode[i], dev->keybit); | |
180 | + __clear_bit(KEY_RESERVED, dev->keybit); | |
181 | + | |
182 | + input_set_capability(dev, EV_MSC, MSC_SCAN); | |
183 | + input_set_drvdata(dev, kbd); | |
184 | + | |
185 | + error = input_register_device(dev); | |
186 | + if (error) | |
187 | + goto fail; | |
188 | + | |
189 | + /* Maple polling is locked to VBLANK - which may be just 50/s */ | |
190 | + maple_getcond_callback(mdev, dc_kbd_callback, HZ/50, MAPLE_FUNC_KEYBOARD); | |
191 | + return 0; | |
192 | + | |
193 | + fail: | |
194 | + input_free_device(dev); | |
195 | + kfree(kbd); | |
196 | + mdev->private_data = NULL; | |
197 | + return error; | |
198 | +} | |
199 | + | |
200 | +static void dc_kbd_disconnect(struct maple_device *mdev) | |
201 | +{ | |
202 | + struct dc_kbd *kbd; | |
203 | + | |
204 | + mutex_lock(&maple_keyb_mutex); | |
205 | + | |
206 | + kbd = mdev->private_data; | |
207 | + mdev->private_data = NULL; | |
208 | + input_unregister_device(kbd->dev); | |
209 | + kfree(kbd); | |
210 | + | |
211 | + mutex_unlock(&maple_keyb_mutex); | |
212 | +} | |
213 | + | |
214 | +/* allow the keyboard to be used */ | |
215 | +static int probe_maple_kbd(struct device *dev) | |
216 | +{ | |
217 | + struct maple_device *mdev = to_maple_dev(dev); | |
218 | + struct maple_driver *mdrv = to_maple_driver(dev->driver); | |
219 | + int error; | |
220 | + | |
221 | + error = dc_kbd_connect(mdev); | |
222 | + if (error) | |
223 | + return error; | |
224 | + | |
225 | + mdev->driver = mdrv; | |
226 | + mdev->registered = 1; | |
227 | + | |
228 | + return 0; | |
229 | +} | |
230 | + | |
231 | +static struct maple_driver dc_kbd_driver = { | |
232 | + .function = MAPLE_FUNC_KEYBOARD, | |
233 | + .connect = dc_kbd_connect, | |
234 | + .disconnect = dc_kbd_disconnect, | |
235 | + .drv = { | |
236 | + .name = "Dreamcast_keyboard", | |
237 | + .probe = probe_maple_kbd, | |
238 | + }, | |
239 | +}; | |
240 | + | |
241 | +static int __init dc_kbd_init(void) | |
242 | +{ | |
243 | + return maple_driver_register(&dc_kbd_driver.drv); | |
244 | +} | |
245 | + | |
246 | +static void __exit dc_kbd_exit(void) | |
247 | +{ | |
248 | + driver_unregister(&dc_kbd_driver.drv); | |
249 | +} | |
250 | + | |
251 | +module_init(dc_kbd_init); | |
252 | +module_exit(dc_kbd_exit); |