bsd_kbd.c revision 8ab4536d
1
2/*
3 * Copyright (c) 2002 by The XFree86 Project, Inc.
4 * Author: Ivan Pascal.
5 *
6 * Based on the code from bsd_io.c which is
7 * Copyright 1992 by Rich Murphey <Rich@Rice.edu>
8 * Copyright 1993 by David Dawes <dawes@xfree86.org>
9 */
10
11#ifdef HAVE_CONFIG_H
12#include "config.h"
13#endif
14
15#include <xorg-server.h>
16#include <X11/X.h>
17#include <termios.h>
18
19#include "compiler.h"
20
21#include "xf86.h"
22#include "xf86Priv.h"
23#include "xf86_OSlib.h"
24
25#include "xf86Xinput.h"
26#include "xf86OSKbd.h"
27#include "atKeynames.h"
28#include "bsd_kbd.h"
29
30static KbdProtocolRec protocols[] = {
31   {"standard", PROT_STD },
32#ifdef WSCONS_SUPPORT
33   {"wskbd", PROT_WSCONS },
34#endif
35   { NULL, PROT_UNKNOWN_KBD }
36};
37
38typedef struct {
39   struct termios kbdtty;
40} BsdKbdPrivRec, *BsdKbdPrivPtr;
41
42static
43int KbdInit(InputInfoPtr pInfo, int what)
44{
45    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
46    BsdKbdPrivPtr priv = (BsdKbdPrivPtr) pKbd->private;
47
48    if (pKbd->isConsole) {
49        switch (pKbd->consType) {
50#if defined(PCCONS_SUPPORT) || defined(SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)  || defined (WSCONS_SUPPORT)
51	    case PCCONS:
52	    case SYSCONS:
53	    case PCVT:
54#if defined WSCONS_SUPPORT
55            case WSCONS:
56#endif
57 	         tcgetattr(pInfo->fd, &(priv->kbdtty));
58#endif
59	         break;
60        }
61    }
62
63    return Success;
64}
65
66static void
67SetKbdLeds(InputInfoPtr pInfo, int leds)
68{
69    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
70    int real_leds = 0;
71
72#ifdef LED_CAP
73    if (leds & XLED1)  real_leds |= LED_CAP;
74#endif
75#ifdef LED_NUM
76    if (leds & XLED2)  real_leds |= LED_NUM;
77#endif
78#ifdef LED_SCR
79    if (leds & XLED3)  real_leds |= LED_SCR;
80    if (leds & XLED4)  real_leds |= LED_SCR;
81#endif
82
83    switch (pKbd->consType) {
84
85#ifdef PCCONS_SUPPORT
86	case PCCONS:
87		break;
88#endif
89#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)
90	case SYSCONS:
91	case PCVT:
92	     ioctl(pInfo->fd, KDSETLED, real_leds);
93	     break;
94#endif
95#if defined(WSCONS_SUPPORT)
96        case WSCONS:
97             ioctl(pInfo->fd, WSKBDIO_SETLEDS, &real_leds);
98             break;
99#endif
100    }
101}
102
103static int
104GetKbdLeds(InputInfoPtr pInfo)
105{
106    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
107    int leds = 0, real_leds = 0;
108
109    switch (pKbd->consType) {
110
111#ifdef PCCONS_SUPPORT
112	case PCCONS:
113	     break;
114#endif
115#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)
116	case SYSCONS:
117	case PCVT:
118	     ioctl(pInfo->fd, KDGETLED, &real_leds);
119	     break;
120#endif
121#if defined(WSCONS_SUPPORT)
122        case WSCONS:
123             ioctl(pInfo->fd, WSKBDIO_GETLEDS, &real_leds);
124             break;
125#endif
126    }
127
128#ifdef LED_CAP
129    if (real_leds & LED_CAP) leds |= XLED1;
130#endif
131#ifdef LED_NUM
132    if (real_leds & LED_NUM) leds |= XLED2;
133#endif
134#ifdef LED_SCR
135    if (real_leds & LED_SCR) leds |= XLED3;
136#endif
137
138    return(leds);
139}
140
141static int
142KbdOn(InputInfoPtr pInfo, int what)
143{
144    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
145#if defined(SYSCONS_SUPPORT) || defined(PCCONS_SUPPORT) || defined(PCVT_SUPPORT) || defined(WSCONS_SUPPORT)
146    BsdKbdPrivPtr priv = (BsdKbdPrivPtr) pKbd->private;
147    struct termios nTty;
148#endif
149#ifdef WSCONS_SUPPORT
150    int option;
151#endif
152
153    if (pKbd->isConsole) {
154        switch (pKbd->consType) {
155
156#if defined(SYSCONS_SUPPORT) || defined(PCCONS_SUPPORT) || defined(PCVT_SUPPORT) || defined(WSCONS_SUPPORT)
157	    case SYSCONS:
158	    case PCCONS:
159	    case PCVT:
160#ifdef WSCONS_SUPPORT
161            case WSCONS:
162#endif
163		 nTty = priv->kbdtty;
164		 nTty.c_iflag = IGNPAR | IGNBRK;
165		 nTty.c_oflag = 0;
166		 nTty.c_cflag = CREAD | CS8;
167		 nTty.c_lflag = 0;
168		 nTty.c_cc[VTIME] = 0;
169		 nTty.c_cc[VMIN] = 1;
170		 cfsetispeed(&nTty, 9600);
171		 cfsetospeed(&nTty, 9600);
172		 if (tcsetattr(pInfo->fd, TCSANOW, &nTty) < 0) {
173			 xf86Msg(X_ERROR, "KbdOn: tcsetattr: %s\n",
174			     strerror(errno));
175		 }
176                 break;
177#endif
178        }
179#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT) || defined (WSCONS_SUPPORT)
180        switch (pKbd->consType) {
181	    case SYSCONS:
182	    case PCVT:
183#ifdef K_CODE
184                 if (pKbd->CustomKeycodes)
185		     ioctl(pInfo->fd, KDSKBMODE, K_CODE);
186	         else
187	             ioctl(pInfo->fd, KDSKBMODE, K_RAW);
188#else
189		 ioctl(pInfo->fd, KDSKBMODE, K_RAW);
190#endif
191	         break;
192#ifdef WSCONS_SUPPORT
193            case WSCONS:
194                 option = WSKBD_RAW;
195                 if (ioctl(pInfo->fd, WSKBDIO_SETMODE, &option) == -1) {
196			 FatalError("can't switch keyboard to raw mode. "
197				    "Enable support for it in the kernel\n"
198				    "or use for example:\n\n"
199				    "Option \"Protocol\" \"wskbd\"\n"
200				    "Option \"Device\" \"/dev/wskbd0\"\n"
201				    "\nin your xorg.conf(5) file\n");
202		 }
203		 break;
204#endif
205        }
206#endif
207    } else {
208        switch (pKbd->consType) {
209#ifdef WSCONS_SUPPORT
210            case WSCONS:
211            	 if ((pKbd->wsKbdDev[0] != 0) && (pInfo->fd == -1)) {
212            	 	xf86Msg(X_INFO, "opening %s\n", pKbd->wsKbdDev);
213            	 	pInfo->fd = open(pKbd->wsKbdDev, O_RDONLY | O_NONBLOCK | O_EXCL);
214#ifdef WSKBDIO_SETVERSION
215		       int version = WSKBDIO_EVENT_VERSION;
216		       if (ioctl(pInfo->fd, WSKBDIO_SETVERSION, &version) == -1) {
217		           xf86Msg(X_WARNING, "%s: cannot set version\n", pInfo->name);
218		           return FALSE;
219		       }
220#endif
221            	 }
222	     break;
223#endif
224	}
225    }
226    return Success;
227}
228
229static int
230KbdOff(InputInfoPtr pInfo, int what)
231{
232    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
233    BsdKbdPrivPtr priv = (BsdKbdPrivPtr) pKbd->private;
234#ifdef WSCONS_SUPPORT
235    int option;
236#endif
237
238    if (pKbd->isConsole) {
239        switch (pKbd->consType) {
240#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)
241	    case SYSCONS:
242	    case PCVT:
243	         ioctl(pInfo->fd, KDSKBMODE, K_XLATE);
244	         /* FALL THROUGH */
245#endif
246#if defined(SYSCONS_SUPPORT) || defined(PCCONS_SUPPORT) || defined(PCVT_SUPPORT)
247	    case PCCONS:
248	         tcsetattr(pInfo->fd, TCSANOW, &(priv->kbdtty));
249	         break;
250#endif
251#ifdef WSCONS_SUPPORT
252            case WSCONS:
253                 option = WSKBD_TRANSLATED;
254                 ioctl(xf86Info.consoleFd, WSKBDIO_SETMODE, &option);
255                 tcsetattr(pInfo->fd, TCSANOW, &(priv->kbdtty));
256	         break;
257#endif
258        }
259    } else {
260         switch (pKbd->consType) {
261#ifdef WSCONS_SUPPORT
262            case WSCONS:
263                 if ((pKbd->wsKbdDev[0] != 0) && (pInfo->fd != -1)) {
264                 	xf86Msg(X_INFO, "closing %s\n", pKbd->wsKbdDev);
265                 	/* need to close the fd while we're gone */
266                 	close(pInfo->fd);
267                 	pInfo->fd = -1;
268                 }
269	         break;
270#endif
271        }
272    }
273    return Success;
274}
275
276static void
277SoundBell(InputInfoPtr pInfo, int loudness, int pitch, int duration)
278{
279    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
280#ifdef WSCONS_SUPPORT
281    struct wskbd_bell_data wsb;
282#endif
283
284    if (loudness && pitch) {
285    	switch (pKbd->consType) {
286#ifdef PCCONS_SUPPORT
287	    case PCCONS:
288	         { int data[2];
289		   data[0] = pitch;
290		   data[1] = (duration * loudness) / 50;
291		   ioctl(pInfo->fd, CONSOLE_X_BELL, data);
292		   break;
293		 }
294#endif
295#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)
296	    case SYSCONS:
297	    case PCVT:
298		 ioctl(pInfo->fd, KDMKTONE,
299		 ((1193190 / pitch) & 0xffff) |
300		 (((unsigned long)duration*loudness/50)<<16));
301		 break;
302#endif
303#if defined (WSCONS_SUPPORT)
304            case WSCONS:
305                 wsb.which = WSKBD_BELL_DOALL;
306                 wsb.pitch = pitch;
307                 wsb.period = duration;
308                 wsb.volume = loudness;
309                 ioctl(pInfo->fd, WSKBDIO_COMPLEXBELL, &wsb);
310                 break;
311#endif
312	}
313    }
314}
315
316static void
317stdReadInput(InputInfoPtr pInfo)
318{
319    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
320    unsigned char rBuf[64];
321    int nBytes, i;
322    if ((nBytes = read( pInfo->fd, (char *)rBuf, sizeof(rBuf))) > 0) {
323       for (i = 0; i < nBytes; i++)
324	   pKbd->PostEvent(pInfo, rBuf[i] & 0x7f,
325                           rBuf[i] & 0x80 ? FALSE : TRUE);
326       }
327}
328
329#ifdef WSCONS_SUPPORT
330
331static void
332WSReadInput(InputInfoPtr pInfo)
333{
334    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
335    struct wscons_event events[64];
336    int type;
337    int blocked, n, i;
338
339    if ((n = read( pInfo->fd, events, sizeof(events))) > 0) {
340        n /=  sizeof(struct wscons_event);
341        for (i = 0; i < n; i++) {
342	    type = events[i].type;
343	    if (type == WSCONS_EVENT_KEY_UP || type == WSCONS_EVENT_KEY_DOWN) {
344		/* It seems better to block SIGIO there */
345		blocked = xf86BlockSIGIO();
346		pKbd->PostEvent(pInfo, (unsigned int)(events[i].value),
347				type == WSCONS_EVENT_KEY_DOWN ? TRUE : FALSE);
348		xf86UnblockSIGIO(blocked);
349	    }
350	} /* for */
351    }
352}
353
354static void
355printWsType(const char *type, char *name)
356{
357    xf86Msg(X_PROBED, "%s: Keyboard type: %s\n", name, type);
358}
359#endif
360
361static Bool
362OpenKeyboard(InputInfoPtr pInfo)
363{
364    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
365    int i;
366    KbdProtocolId prot = PROT_UNKNOWN_KBD;
367    char *s;
368
369    s = xf86SetStrOption(pInfo->options, "Protocol", NULL);
370    for (i = 0; protocols[i].name; i++) {
371        if (xf86NameCmp(s, protocols[i].name) == 0) {
372           prot = protocols[i].id;
373           break;
374        }
375    }
376
377    switch (prot) {
378    	case PROT_STD:
379           pInfo->read_input = stdReadInput;
380           break;
381#ifdef WSCONS_SUPPORT
382        case PROT_WSCONS:
383           pInfo->read_input = WSReadInput;
384           break;
385#endif
386        default:
387           xf86Msg(X_ERROR,"\"%s\" is not a valid keyboard protocol name\n", s);
388           free(s);
389           return FALSE;
390    }
391    free(s);
392
393    if (prot == PROT_WSCONS) {
394	s = xf86SetStrOption(pInfo->options, "Device", "/dev/wskbd");
395    } else
396	s = xf86SetStrOption(pInfo->options, "Device", NULL);
397
398    if (s == NULL) {
399	pInfo->fd = xf86Info.consoleFd;
400	pKbd->isConsole = TRUE;
401	pKbd->consType = xf86Info.consType;
402	pKbd->wsKbdDev[0] = 0;
403    } else {
404	pInfo->fd = open(s, O_RDONLY | O_NONBLOCK | O_EXCL);
405	if (pInfo->fd == -1) {
406           xf86Msg(X_ERROR, "%s: cannot open \"%s\"\n", pInfo->name, s);
407           free(s);
408           return FALSE;
409       }
410       pKbd->isConsole = FALSE;
411       strncpy(pKbd->wsKbdDev, s, 256);
412       pKbd->consType = xf86Info.consType;
413       free(s);
414    }
415
416#ifdef WSCONS_SUPPORT
417    if( prot == PROT_WSCONS) {
418       pKbd->consType = WSCONS;
419#ifdef WSKBDIO_SETVERSION
420       int version = WSKBDIO_EVENT_VERSION;
421       if (ioctl(pInfo->fd, WSKBDIO_SETVERSION, &version) == -1) {
422           xf86Msg(X_WARNING, "%s: cannot set version\n", pInfo->name);
423           return FALSE;
424       }
425#endif
426       /* Find out keyboard type */
427       if (ioctl(pInfo->fd, WSKBDIO_GTYPE, &(pKbd->wsKbdType)) == -1) {
428           xf86Msg(X_ERROR, "%s: cannot get keyboard type", pInfo->name);
429           close(pInfo->fd);
430           return FALSE;
431       }
432       switch (pKbd->wsKbdType) {
433           case WSKBD_TYPE_PC_XT:
434               printWsType("XT", pInfo->name);
435               break;
436           case WSKBD_TYPE_PC_AT:
437               printWsType("AT", pInfo->name);
438               break;
439           case WSKBD_TYPE_USB:
440               printWsType("USB", pInfo->name);
441               break;
442#ifdef WSKBD_TYPE_ADB
443           case WSKBD_TYPE_ADB:
444               printWsType("ADB", pInfo->name);
445               break;
446#endif
447#ifdef WSKBD_TYPE_LK201
448           case WSKBD_TYPE_LK201:
449               printWsType("LK201", pInfo->name);
450               break;
451#endif
452#ifdef WSKBD_TYPE_MAPLE
453           case WSKBD_TYPE_MAPLE:
454               printWsType("Maple", pInfo->name);
455               break;
456#endif
457#ifdef WSKBD_TYPE_SUN
458           case WSKBD_TYPE_SUN:
459               printWsType("Sun", pInfo->name);
460               break;
461#endif
462#ifdef WSKBD_TYPE_SUN5
463           case WSKBD_TYPE_SUN5:
464               printWsType("Sun5", pInfo->name);
465               break;
466#endif
467           default:
468               xf86Msg(X_WARNING, "%s: Unsupported wskbd type \"%d\"\n",
469                                  pInfo->name, pKbd->wsKbdType);
470               printWsType("Unknown wskbd", pInfo->name);
471               break;
472       }
473    }
474#endif
475
476#if 0	/* no more vtSwitchSupported in xf86-input-keyboard-1.6.0 */
477#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT) || defined (WSCONS_SUPPORT)
478    if ((pKbd->isConsole &&
479         ((pKbd->consType == SYSCONS) || (pKbd->consType == PCVT))) ||
480	(pKbd->consType == WSCONS))
481        pKbd->vtSwitchSupported = TRUE;
482#endif
483#endif
484
485    return TRUE;
486}
487
488_X_EXPORT Bool
489xf86OSKbdPreInit(InputInfoPtr pInfo)
490{
491    KbdDevPtr pKbd = pInfo->private;
492
493    pKbd->KbdInit	= KbdInit;
494    pKbd->KbdOn		= KbdOn;
495    pKbd->KbdOff	= KbdOff;
496    pKbd->Bell		= SoundBell;
497    pKbd->SetLeds	= SetKbdLeds;
498    pKbd->GetLeds	= GetKbdLeds;
499    pKbd->KbdGetMapping	= KbdGetMapping;
500
501    pKbd->RemapScanCode = NULL;
502
503    pKbd->OpenKeyboard = OpenKeyboard;
504
505    pKbd->private = calloc(sizeof(BsdKbdPrivRec), 1);
506    if (pKbd->private == NULL) {
507       xf86Msg(X_ERROR,"can't allocate keyboard OS private data\n");
508       return FALSE;
509    }
510    return TRUE;
511}
512