bsd_kbd.c revision ee3138f1
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#endif
210#ifdef WSCONS_SUPPORT
211            case WSCONS:
212                 option = WSKBD_RAW;
213                 if (ioctl(pInfo->fd, WSKBDIO_SETMODE, &option) == -1) {
214			 FatalError("can't switch keyboard to raw mode. "
215				    "Enable support for it in the kernel\n"
216				    "or use for example:\n\n"
217				    "Option \"Protocol\" \"wskbd\"\n"
218				    "Option \"Device\" \"/dev/wskbd0\"\n"
219				    "\nin your xorg.conf(5) file\n");
220		 }
221		 break;
222#endif
223        }
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#endif
340         }
341      }
342    }
343
344    return FALSE;
345}
346
347static void
348stdReadInput(InputInfoPtr pInfo)
349{
350    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
351    unsigned char rBuf[64];
352    int nBytes, i;
353    if ((nBytes = read( pInfo->fd, (char *)rBuf, sizeof(rBuf))) > 0) {
354       for (i = 0; i < nBytes; i++)
355	   pKbd->PostEvent(pInfo, rBuf[i] & 0x7f,
356                           rBuf[i] & 0x80 ? FALSE : TRUE);
357       }
358}
359
360#ifdef WSCONS_SUPPORT
361
362static void
363WSReadInput(InputInfoPtr pInfo)
364{
365    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
366    struct wscons_event events[64];
367    int type;
368    int blocked, n, i;
369
370    if ((n = read( pInfo->fd, events, sizeof(events))) > 0) {
371        n /=  sizeof(struct wscons_event);
372        for (i = 0; i < n; i++) {
373	    type = events[i].type;
374	    if (type == WSCONS_EVENT_KEY_UP || type == WSCONS_EVENT_KEY_DOWN) {
375		/* It seems better to block SIGIO there */
376		blocked = xf86BlockSIGIO();
377		pKbd->PostEvent(pInfo, (unsigned int)(events[i].value),
378				type == WSCONS_EVENT_KEY_DOWN ? TRUE : FALSE);
379		xf86UnblockSIGIO(blocked);
380	    }
381	} /* for */
382    }
383}
384
385static void
386printWsType(char *type, char *devname)
387{
388    xf86Msg(X_PROBED, "%s: Keyboard type: %s\n", devname, type);
389}
390#endif
391
392static Bool
393OpenKeyboard(InputInfoPtr pInfo)
394{
395    KbdDevPtr pKbd = (KbdDevPtr) pInfo->private;
396    int i;
397    KbdProtocolId prot = PROT_UNKNOWN_KBD;
398    char *s;
399
400    s = xf86SetStrOption(pInfo->options, "Protocol", NULL);
401    for (i = 0; protocols[i].name; i++) {
402        if (xf86NameCmp(s, protocols[i].name) == 0) {
403           prot = protocols[i].id;
404           break;
405        }
406    }
407
408    switch (prot) {
409    	case PROT_STD:
410           pInfo->read_input = stdReadInput;
411           break;
412#ifdef WSCONS_SUPPORT
413        case PROT_WSCONS:
414           pInfo->read_input = WSReadInput;
415           break;
416#endif
417        default:
418           xf86Msg(X_ERROR,"\"%s\" is not a valid keyboard protocol name\n", s);
419           xfree(s);
420           return FALSE;
421    }
422    xf86Msg(X_CONFIG, "%s: Protocol: %s\n", pInfo->name, s);
423    xfree(s);
424
425    s = xf86SetStrOption(pInfo->options, "Device", NULL);
426    if (s == NULL) {
427       if (prot == PROT_WSCONS) {
428           xf86Msg(X_ERROR,"A \"device\" option is required with"
429                                  " the \"wskbd\" keyboard protocol\n");
430           return FALSE;
431       } else {
432           pInfo->fd = xf86Info.consoleFd;
433           pKbd->isConsole = TRUE;
434           pKbd->consType = xf86Info.consType;
435       }
436    } else {
437	pInfo->fd = open(s, O_RDONLY | O_NONBLOCK | O_EXCL);
438       if (pInfo->fd == -1) {
439           xf86Msg(X_ERROR, "%s: cannot open \"%s\"\n", pInfo->name, s);
440           xfree(s);
441           return FALSE;
442       }
443       pKbd->isConsole = FALSE;
444       pKbd->consType = xf86Info.consType;
445       xfree(s);
446    }
447
448#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)
449    if (pKbd->isConsole &&
450        ((pKbd->consType == SYSCONS) || (pKbd->consType == PCVT)))
451        pKbd->vtSwitchSupported = TRUE;
452#endif
453
454#ifdef WSCONS_SUPPORT
455    if( prot == PROT_WSCONS) {
456       pKbd->consType = WSCONS;
457       /* Find out keyboard type */
458       if (ioctl(pInfo->fd, WSKBDIO_GTYPE, &(pKbd->wsKbdType)) == -1) {
459           xf86Msg(X_ERROR, "%s: cannot get keyboard type", pInfo->name);
460           close(pInfo->fd);
461           return FALSE;
462       }
463       switch (pKbd->wsKbdType) {
464           case WSKBD_TYPE_PC_XT:
465               printWsType("XT", pInfo->name);
466               break;
467           case WSKBD_TYPE_PC_AT:
468               printWsType("AT", pInfo->name);
469               break;
470           case WSKBD_TYPE_USB:
471               printWsType("USB", pInfo->name);
472               break;
473#ifdef WSKBD_TYPE_ADB
474           case WSKBD_TYPE_ADB:
475               printWsType("ADB", pInfo->name);
476               break;
477#endif
478#ifdef WSKBD_TYPE_SUN
479           case WSKBD_TYPE_SUN:
480               printWsType("Sun", pInfo->name);
481               break;
482#endif
483#ifdef WSKBD_TYPE_SUN5
484     case WSKBD_TYPE_SUN5:
485	     xf86Msg(X_PROBED, "Keyboard type: Sun5\n");
486	     break;
487#endif
488           default:
489               xf86Msg(X_ERROR, "%s: Unsupported wskbd type \"%d\"",
490                                pInfo->name, pKbd->wsKbdType);
491               close(pInfo->fd);
492               return FALSE;
493       }
494    }
495#endif
496    return TRUE;
497}
498
499_X_EXPORT Bool
500xf86OSKbdPreInit(InputInfoPtr pInfo)
501{
502    KbdDevPtr pKbd = pInfo->private;
503
504    pKbd->KbdInit	= KbdInit;
505    pKbd->KbdOn		= KbdOn;
506    pKbd->KbdOff	= KbdOff;
507    pKbd->Bell		= SoundBell;
508    pKbd->SetLeds	= SetKbdLeds;
509    pKbd->GetLeds	= GetKbdLeds;
510    pKbd->SetKbdRepeat	= SetKbdRepeat;
511    pKbd->KbdGetMapping	= KbdGetMapping;
512    pKbd->SpecialKey	= SpecialKey;
513
514    pKbd->RemapScanCode = NULL;
515    pKbd->GetSpecialKey = NULL;
516
517    pKbd->OpenKeyboard = OpenKeyboard;
518    pKbd->vtSwitchSupported = FALSE;
519    pKbd->CustomKeycodes = FALSE;
520
521    pKbd->private = xcalloc(sizeof(BsdKbdPrivRec), 1);
522    if (pKbd->private == NULL) {
523       xf86Msg(X_ERROR,"can't allocate keyboard OS private data\n");
524       return FALSE;
525    }
526    return TRUE;
527}
528