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