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