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