ukbd.c revision 1.2 1 /* $NetBSD: ukbd.c,v 1.2 1998/07/25 15:36:30 augustss Exp $ */
2
3 /*
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Author: Lennart Augustsson <augustss (at) carlstedt.se>
8 * Carlstedt Research & Technology
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>
44 #include <sys/device.h>
45 #include <sys/ioctl.h>
46 #include <sys/tty.h>
47 #include <sys/file.h>
48 #include <sys/select.h>
49 #include <sys/proc.h>
50 #include <sys/vnode.h>
51 #include <sys/device.h>
52 #include <sys/poll.h>
53
54 #include <dev/usb/usb.h>
55 #include <dev/usb/usbhid.h>
56 #include <dev/usb/usbdi.h>
57 #include <dev/usb/usbdi_util.h>
58 #include <dev/usb/usbdevs.h>
59 #include <dev/usb/usb_quirks.h>
60 #include <dev/usb/hid.h>
61
62 #include <dev/wscons/wsconsio.h>
63 #include <dev/wscons/wskbdvar.h>
64 #include <dev/wscons/wsksymdef.h>
65 #include <dev/wscons/wsksymvar.h>
66 #include <dev/wscons/wskbdmap_mfii.h>
67
68 #include "opt_pckbd_layout.h"
69 #include "opt_wsdisplay_compat.h"
70
71 #ifdef USB_DEBUG
72 #define DPRINTF(x) if (ukbddebug) printf x
73 #define DPRINTFN(n,x) if (ukbddebug>(n)) printf x
74 int ukbddebug = 0;
75 #else
76 #define DPRINTF(x)
77 #define DPRINTFN(n,x)
78 #endif
79
80 #define UPROTO_BOOT_KEYBOARD 1
81
82 #define NKEYCODE 6
83
84 #define NUM_LOCK 0x01
85 #define CAPS_LOCK 0x02
86 #define SCROLL_LOCK 0x04
87
88 struct ukbd_data {
89 u_int8_t modifiers;
90 #define MOD_CONTROL_L 0x01
91 #define MOD_CONTROL_R 0x10
92 #define MOD_SHIFT_L 0x02
93 #define MOD_SHIFT_R 0x20
94 #define MOD_ALT_L 0x04
95 #define MOD_ALT_R 0x40
96 #define MOD_WIN_L 0x08
97 #define MOD_WIN_R 0x80
98 u_int8_t reserved;
99 u_int8_t keycode[NKEYCODE];
100 };
101
102 #define PRESS 0
103 #define RELEASE 0x80
104
105 #define NMOD 6
106 static struct {
107 int mask, key;
108 } ukbd_mods[NMOD] = {
109 { MOD_CONTROL_L, 29 },
110 { MOD_CONTROL_R, 58 },
111 { MOD_SHIFT_L, 42 },
112 { MOD_SHIFT_R, 54 },
113 { MOD_ALT_L, 56 },
114 { MOD_ALT_R, 56 },
115 };
116
117 #define XX 0
118 /* Translate USB keycodes to US keyboard scancodes. */
119 /* XXX very incomplete */
120 static char ukbd_trtab[256] = {
121 0, 0, 0, 0, 30, 48, 46, 32,
122 18, 33, 34, 35, 23, 36, 37, 38,
123 50, 49, 24, 25, 16, 19, 31, 20,
124 22, 47, 17, 45, 21, 44, 2, 3,
125 4, 5, 6, 7, 8, 9, 10, 11,
126 28, 1, 14, 15, 57, 12, 13, 26,
127 27, 43, XX, 39, 40, 41, 51, 52,
128 53, 58, 59, 60, 61, 62, 63, 64,
129 65, 66, 67, 68, 87, 88, XX, 70,
130 XX, XX, XX, XX, XX,
131 };
132
133 #define KEY_ERROR 0x01
134
135 struct ukbd_softc {
136 struct device sc_dev; /* base device */
137 usbd_interface_handle sc_iface; /* interface */
138 usbd_pipe_handle sc_intrpipe; /* interrupt pipe */
139 int sc_ep_addr;
140
141 struct ukbd_data sc_ndata;
142 struct ukbd_data sc_odata;
143
144 int sc_state;
145 #define UKBD_NEEDCLEAR 0x01 /* needs clearing endpoint stall */
146 int sc_disconnected; /* device is gone */
147
148 int sc_leds;
149 struct device *sc_wskbddev;
150 #ifdef WSDISPLAY_COMPAT_RAWKBD
151 int sc_rawkbd;
152 #endif
153 };
154
155 #define UKBDUNIT(dev) (minor(dev))
156 #define UKBD_CHUNK 128 /* chunk size for read */
157 #define UKBD_BSIZE 1020 /* buffer size */
158
159 int ukbd_match __P((struct device *, struct cfdata *, void *));
160 void ukbd_attach __P((struct device *, struct device *, void *));
161
162 void ukbd_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
163 void ukbd_disco __P((void *));
164
165 void ukbd_set_leds __P((void *, int));
166 int ukbd_ioctl __P((void *, u_long, caddr_t, int, struct proc *));
167
168 extern struct cfdriver ukbd_cd;
169
170 struct cfattach ukbd_ca = {
171 sizeof(struct ukbd_softc), ukbd_match, ukbd_attach
172 };
173
174 int
175 ukbd_match(parent, match, aux)
176 struct device *parent;
177 struct cfdata *match;
178 void *aux;
179 {
180 struct usb_attach_arg *uaa = (struct usb_attach_arg *)aux;
181 usb_interface_descriptor_t *id;
182
183 /* Check that this is a keyboard that speaks the boot protocol. */
184 if (!uaa->iface)
185 return (UMATCH_NONE);
186 id = usbd_get_interface_descriptor(uaa->iface);
187 if (id->bInterfaceClass != UCLASS_HID ||
188 id->bInterfaceSubClass != USUBCLASS_BOOT ||
189 id->bInterfaceProtocol != UPROTO_BOOT_KEYBOARD)
190 return (UMATCH_NONE);
191 return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
192 }
193
194 void
195 ukbd_attach(parent, self, aux)
196 struct device *parent;
197 struct device *self;
198 void *aux;
199 {
200 struct ukbd_softc *sc = (struct ukbd_softc *)self;
201 struct usb_attach_arg *uaa = aux;
202 usbd_interface_handle iface = uaa->iface;
203 usb_interface_descriptor_t *id;
204 usb_endpoint_descriptor_t *ed;
205 usbd_status r;
206 char devinfo[1024];
207 struct wskbddev_attach_args a;
208
209 sc->sc_disconnected = 1;
210 sc->sc_iface = iface;
211 id = usbd_get_interface_descriptor(iface);
212 usbd_devinfo(uaa->device, 0, devinfo);
213 printf(": %s (interface class %d/%d)\n", devinfo,
214 id->bInterfaceClass, id->bInterfaceSubClass);
215 ed = usbd_interface2endpoint_descriptor(iface, 0);
216 if (!ed) {
217 printf("%s: could not read endpoint descriptor\n",
218 sc->sc_dev.dv_xname);
219 return;
220 }
221
222 DPRINTFN(10,("ukbd_attach: \
223 bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n",
224 ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR,
225 ed->bEndpointAddress & UE_IN ? "in" : "out",
226 ed->bmAttributes & UE_XFERTYPE,
227 UGETW(ed->wMaxPacketSize), ed->bInterval));
228
229 if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
230 (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
231 printf("%s: unexpected endpoint\n",
232 sc->sc_dev.dv_xname);
233 return;
234 }
235
236 if ((usbd_get_quirks(uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) {
237 r = usbd_set_protocol(iface, 0);
238 DPRINTFN(5, ("ukbd_attach: protocol set\n"));
239 if (r != USBD_NORMAL_COMPLETION) {
240 printf("%s: set protocol failed\n",
241 sc->sc_dev.dv_xname);
242 return;
243 }
244 }
245
246 sc->sc_ep_addr = ed->bEndpointAddress;
247 sc->sc_disconnected = 0;
248
249 a.console = 0; /* XXX */
250 #ifdef PCKBD_LAYOUT
251 a.layout = PCKBD_LAYOUT;
252 #else
253 a.layout = KB_US;
254 #endif
255 a.keydesc = pckbd_keydesctab;
256 a.num_keydescs = sizeof(pckbd_keydesctab)/sizeof(pckbd_keydesctab[0]);
257 a.getc = NULL;
258 a.pollc = NULL;
259 a.set_leds = ukbd_set_leds;
260 a.ioctl = ukbd_ioctl;
261 a.accesscookie = sc;
262 sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
263
264 /* Set up interrupt pipe. */
265 r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
266 USBD_SHORT_XFER_OK,
267 &sc->sc_intrpipe, sc, &sc->sc_ndata,
268 sizeof(sc->sc_ndata), ukbd_intr);
269 if (r != USBD_NORMAL_COMPLETION) {
270 printf("%s: usbd_open_pipe_intr failed, error=%d\n",
271 sc->sc_dev.dv_xname, r);
272 return;
273 }
274 usbd_set_disco(sc->sc_intrpipe, ukbd_disco, sc);
275 }
276
277 void
278 ukbd_disco(p)
279 void *p;
280 {
281 struct ukbd_softc *sc = p;
282 sc->sc_disconnected = 1;
283 }
284
285 void
286 ukbd_intr(reqh, addr, status)
287 usbd_request_handle reqh;
288 usbd_private_handle addr;
289 usbd_status status;
290 {
291 struct ukbd_softc *sc = addr;
292 struct ukbd_data *ud = &sc->sc_ndata;
293 int mod, omod;
294 char ibuf[NMOD+2*NKEYCODE]; /* chars events */
295 int nkeys, i, j;
296 int key, c;
297 #define ADDKEY(c) ibuf[nkeys++] = (c)
298
299 DPRINTFN(5, ("ukbd_intr: status=%d\n", status));
300 if (status == USBD_CANCELLED)
301 return;
302
303 if (status != USBD_NORMAL_COMPLETION) {
304 DPRINTF(("ukbd_intr: status=%d\n", status));
305 sc->sc_state |= UKBD_NEEDCLEAR;
306 return;
307 }
308
309 DPRINTFN(5, (" mod=0x%02x key0=0x%02x key1=0x%02x\n",
310 ud->modifiers, ud->keycode[0], ud->keycode[1]));
311
312 if (ud->keycode[0] == KEY_ERROR)
313 return; /* ignore */
314 nkeys = 0;
315 mod = ud->modifiers;
316 omod = sc->sc_odata.modifiers;
317 if (mod != omod)
318 for (i = 0; i < NMOD; i++)
319 if (( mod & ukbd_mods[i].mask) !=
320 (omod & ukbd_mods[i].mask))
321 ADDKEY(ukbd_mods[i].key |
322 (mod & ukbd_mods[i].mask
323 ? PRESS : RELEASE));
324 if (memcmp(ud->keycode, sc->sc_odata.keycode, NKEYCODE) != 0) {
325 /* Check for released keys. */
326 for (i = 0; i < NKEYCODE; i++) {
327 key = sc->sc_odata.keycode[i];
328 if (key == 0)
329 continue;
330 for (j = 0; j < NKEYCODE; j++)
331 if (key == ud->keycode[j])
332 goto rfound;
333 c = ukbd_trtab[key];
334 if (c)
335 ADDKEY(c | RELEASE);
336 rfound:
337 ;
338 }
339
340 /* Check for pressed keys. */
341 for (i = 0; i < NKEYCODE; i++) {
342 key = ud->keycode[i];
343 if (key == 0)
344 continue;
345 for (j = 0; j < NKEYCODE; j++)
346 if (key == sc->sc_odata.keycode[j])
347 goto pfound;
348 c = ukbd_trtab[key];
349 if (c)
350 ADDKEY(c | PRESS);
351 pfound:
352 ;
353 }
354 }
355 sc->sc_odata = *ud;
356
357 for (i = 0; i < nkeys; i++) {
358 c = ibuf[i];
359 wskbd_input(sc->sc_wskbddev,
360 c&0x80 ? WSCONS_EVENT_KEY_UP:WSCONS_EVENT_KEY_DOWN,
361 c & 0x7f);
362 }
363 }
364
365 void
366 ukbd_set_leds(v, leds)
367 void *v;
368 int leds;
369 {
370 struct ukbd_softc *sc = v;
371 u_int8_t res;
372
373 DPRINTF(("ukbd_set_leds: sc=%p leds=%d\n", sc, leds));
374
375 sc->sc_leds = leds;
376 res = 0;
377 if (leds & WSKBD_LED_SCROLL)
378 res |= SCROLL_LOCK;
379 if (leds & WSKBD_LED_NUM)
380 res |= NUM_LOCK;
381 if (leds & WSKBD_LED_CAPS)
382 res |= CAPS_LOCK;
383 usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, 0, &res, 1);
384 }
385
386 int
387 ukbd_ioctl(v, cmd, data, flag, p)
388 void *v;
389 u_long cmd;
390 caddr_t data;
391 int flag;
392 struct proc *p;
393 {
394 struct ukbd_softc *sc = v;
395
396 switch (cmd) {
397 case WSKBDIO_GTYPE:
398 *(int *)data = WSKBD_TYPE_PC_XT;
399 return 0;
400 case WSKBDIO_SETLEDS:
401 ukbd_set_leds(v, *(int *)data);
402 return 0;
403 case WSKBDIO_GETLEDS:
404 *(int *)data = sc->sc_leds;
405 return (0);
406 #ifdef WSDISPLAY_COMPAT_RAWKBD
407 case WSKBDIO_SETMODE:
408 sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
409 return (0);
410 #endif
411 }
412 return -1;
413 }
414
415