The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/drivers/char/pc_keyb.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * linux/drivers/char/pc_keyb.c
    3  *
    4  * Separation of the PC low-level part by Geert Uytterhoeven, May 1997
    5  * See keyboard.c for the whole history.
    6  *
    7  * Major cleanup by Martin Mares, May 1997
    8  *
    9  * Combined the keyboard and PS/2 mouse handling into one file,
   10  * because they share the same hardware.
   11  * Johan Myreen <jem@iki.fi> 1998-10-08.
   12  *
   13  * Code fixes to handle mouse ACKs properly.
   14  * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29.
   15  *
   16  */
   17 
   18 #include <linux/config.h>
   19 
   20 #include <linux/spinlock.h>
   21 #include <linux/sched.h>
   22 #include <linux/interrupt.h>
   23 #include <linux/tty.h>
   24 #include <linux/mm.h>
   25 #include <linux/signal.h>
   26 #include <linux/init.h>
   27 #include <linux/kbd_ll.h>
   28 #include <linux/delay.h>
   29 #include <linux/random.h>
   30 #include <linux/poll.h>
   31 #include <linux/miscdevice.h>
   32 #include <linux/slab.h>
   33 #include <linux/kbd_kern.h>
   34 #include <linux/vt_kern.h>
   35 #include <linux/smp_lock.h>
   36 #include <linux/kd.h>
   37 #include <linux/pm.h>
   38 
   39 #include <asm/keyboard.h>
   40 #include <asm/bitops.h>
   41 #include <asm/uaccess.h>
   42 #include <asm/irq.h>
   43 #include <asm/system.h>
   44 
   45 #include <asm/io.h>
   46 
   47 /* Some configuration switches are present in the include file... */
   48 
   49 #include <linux/pc_keyb.h>
   50 
   51 /* Simple translation table for the SysRq keys */
   52 
   53 #ifdef CONFIG_MAGIC_SYSRQ
   54 unsigned char pckbd_sysrq_xlate[128] =
   55         "\000\0331234567890-=\177\t"                    /* 0x00 - 0x0f */
   56         "qwertyuiop[]\r\000as"                          /* 0x10 - 0x1f */
   57         "dfghjkl;'`\000\\zxcv"                          /* 0x20 - 0x2f */
   58         "bnm,./\000*\000 \000\201\202\203\204\205"      /* 0x30 - 0x3f */
   59         "\206\207\210\211\212\000\000789-456+1"         /* 0x40 - 0x4f */
   60         "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */
   61         "\r\000/";                                      /* 0x60 - 0x6f */
   62 #endif
   63 
   64 static void kbd_write_command_w(int data);
   65 static void kbd_write_output_w(int data);
   66 #ifdef CONFIG_PSMOUSE
   67 static void aux_write_ack(int val);
   68 static void __aux_write_ack(int val);
   69 static int aux_reconnect = 0;
   70 #endif
   71 
   72 #ifndef kbd_controller_present
   73 #define kbd_controller_present()        1
   74 #endif
   75 static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
   76 static unsigned char handle_kbd_event(void);
   77 
   78 /* used only by send_data - set by keyboard_interrupt */
   79 static volatile unsigned char reply_expected;
   80 static volatile unsigned char acknowledge;
   81 static volatile unsigned char resend;
   82 
   83 
   84 #if defined CONFIG_PSMOUSE
   85 /*
   86  *      PS/2 Auxiliary Device
   87  */
   88 
   89 static int __init psaux_init(void);
   90 
   91 #define AUX_RECONNECT1 0xaa     /* scancode1 when ps2 device is plugged (back) in */
   92 #define AUX_RECONNECT2 0x00     /* scancode2 when ps2 device is plugged (back) in */
   93  
   94 static struct aux_queue *queue; /* Mouse data buffer. */
   95 static int aux_count;
   96 /* used when we send commands to the mouse that expect an ACK. */
   97 static unsigned char mouse_reply_expected;
   98 
   99 #define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT)
  100 #define AUX_INTS_ON  (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT)
  101 
  102 #define MAX_RETRIES     60              /* some aux operations take long time*/
  103 #endif /* CONFIG_PSMOUSE */
  104 
  105 /*
  106  * Wait for keyboard controller input buffer to drain.
  107  *
  108  * Don't use 'jiffies' so that we don't depend on
  109  * interrupts..
  110  *
  111  * Quote from PS/2 System Reference Manual:
  112  *
  113  * "Address hex 0060 and address hex 0064 should be written only when
  114  * the input-buffer-full bit and output-buffer-full bit in the
  115  * Controller Status register are set 0."
  116  */
  117 
  118 static void kb_wait(void)
  119 {
  120         unsigned long timeout = KBC_TIMEOUT;
  121 
  122         do {
  123                 /*
  124                  * "handle_kbd_event()" will handle any incoming events
  125                  * while we wait - keypresses or mouse movement.
  126                  */
  127                 unsigned char status = handle_kbd_event();
  128 
  129                 if (! (status & KBD_STAT_IBF))
  130                         return;
  131                 mdelay(1);
  132                 timeout--;
  133         } while (timeout);
  134 #ifdef KBD_REPORT_TIMEOUTS
  135         printk(KERN_WARNING "Keyboard timed out[1]\n");
  136 #endif
  137 }
  138 
  139 /*
  140  * Translation of escaped scancodes to keycodes.
  141  * This is now user-settable.
  142  * The keycodes 1-88,96-111,119 are fairly standard, and
  143  * should probably not be changed - changing might confuse X.
  144  * X also interprets scancode 0x5d (KEY_Begin).
  145  *
  146  * For 1-88 keycode equals scancode.
  147  */
  148 
  149 #define E0_KPENTER 96
  150 #define E0_RCTRL   97
  151 #define E0_KPSLASH 98
  152 #define E0_PRSCR   99
  153 #define E0_RALT    100
  154 #define E0_BREAK   101  /* (control-pause) */
  155 #define E0_HOME    102
  156 #define E0_UP      103
  157 #define E0_PGUP    104
  158 #define E0_LEFT    105
  159 #define E0_RIGHT   106
  160 #define E0_END     107
  161 #define E0_DOWN    108
  162 #define E0_PGDN    109
  163 #define E0_INS     110
  164 #define E0_DEL     111
  165 
  166 #define E1_PAUSE   119
  167 
  168 /*
  169  * The keycodes below are randomly located in 89-95,112-118,120-127.
  170  * They could be thrown away (and all occurrences below replaced by 0),
  171  * but that would force many users to use the `setkeycodes' utility, where
  172  * they needed not before. It does not matter that there are duplicates, as
  173  * long as no duplication occurs for any single keyboard.
  174  */
  175 #define SC_LIM 89
  176 
  177 #define FOCUS_PF1 85           /* actual code! */
  178 #define FOCUS_PF2 89
  179 #define FOCUS_PF3 90
  180 #define FOCUS_PF4 91
  181 #define FOCUS_PF5 92
  182 #define FOCUS_PF6 93
  183 #define FOCUS_PF7 94
  184 #define FOCUS_PF8 95
  185 #define FOCUS_PF9 120
  186 #define FOCUS_PF10 121
  187 #define FOCUS_PF11 122
  188 #define FOCUS_PF12 123
  189 
  190 #define JAP_86     124
  191 /* tfj@olivia.ping.dk:
  192  * The four keys are located over the numeric keypad, and are
  193  * labelled A1-A4. It's an rc930 keyboard, from
  194  * Regnecentralen/RC International, Now ICL.
  195  * Scancodes: 59, 5a, 5b, 5c.
  196  */
  197 #define RGN1 124
  198 #define RGN2 125
  199 #define RGN3 126
  200 #define RGN4 127
  201 
  202 static unsigned char high_keys[128 - SC_LIM] = {
  203   RGN1, RGN2, RGN3, RGN4, 0, 0, 0,                   /* 0x59-0x5f */
  204   0, 0, 0, 0, 0, 0, 0, 0,                            /* 0x60-0x67 */
  205   0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12,          /* 0x68-0x6f */
  206   0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3,    /* 0x70-0x77 */
  207   FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7,        /* 0x78-0x7b */
  208   FOCUS_PF8, JAP_86, FOCUS_PF10, 0                   /* 0x7c-0x7f */
  209 };
  210 
  211 /* BTC */
  212 #define E0_MACRO   112
  213 /* LK450 */
  214 #define E0_F13     113
  215 #define E0_F14     114
  216 #define E0_HELP    115
  217 #define E0_DO      116
  218 #define E0_F17     117
  219 #define E0_KPMINPLUS 118
  220 /*
  221  * My OmniKey generates e0 4c for  the "OMNI" key and the
  222  * right alt key does nada. [kkoller@nyx10.cs.du.edu]
  223  */
  224 #define E0_OK   124
  225 /*
  226  * New microsoft keyboard is rumoured to have
  227  * e0 5b (left window button), e0 5c (right window button),
  228  * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU]
  229  * [or: Windows_L, Windows_R, TaskMan]
  230  */
  231 #define E0_MSLW 125
  232 #define E0_MSRW 126
  233 #define E0_MSTM 127
  234 
  235 static unsigned char e0_keys[128] = {
  236   0, 0, 0, 0, 0, 0, 0, 0,                             /* 0x00-0x07 */
  237   0, 0, 0, 0, 0, 0, 0, 0,                             /* 0x08-0x0f */
  238   0, 0, 0, 0, 0, 0, 0, 0,                             /* 0x10-0x17 */
  239   0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0,             /* 0x18-0x1f */
  240   0, 0, 0, 0, 0, 0, 0, 0,                             /* 0x20-0x27 */
  241   0, 0, 0, 0, 0, 0, 0, 0,                             /* 0x28-0x2f */
  242   0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR,             /* 0x30-0x37 */
  243   E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP,       /* 0x38-0x3f */
  244   E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME,       /* 0x40-0x47 */
  245   E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */
  246   E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0,       /* 0x50-0x57 */
  247   0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0,           /* 0x58-0x5f */
  248   0, 0, 0, 0, 0, 0, 0, 0,                             /* 0x60-0x67 */
  249   0, 0, 0, 0, 0, 0, 0, E0_MACRO,                      /* 0x68-0x6f */
  250   0, 0, 0, 0, 0, 0, 0, 0,                             /* 0x70-0x77 */
  251   0, 0, 0, 0, 0, 0, 0, 0                              /* 0x78-0x7f */
  252 };
  253 
  254 int pckbd_setkeycode(unsigned int scancode, unsigned int keycode)
  255 {
  256         if (scancode < SC_LIM || scancode > 255 || keycode > 127)
  257           return -EINVAL;
  258         if (scancode < 128)
  259           high_keys[scancode - SC_LIM] = keycode;
  260         else
  261           e0_keys[scancode - 128] = keycode;
  262         return 0;
  263 }
  264 
  265 int pckbd_getkeycode(unsigned int scancode)
  266 {
  267         return
  268           (scancode < SC_LIM || scancode > 255) ? -EINVAL :
  269           (scancode < 128) ? high_keys[scancode - SC_LIM] :
  270             e0_keys[scancode - 128];
  271 }
  272 
  273 static int do_acknowledge(unsigned char scancode)
  274 {
  275         if (reply_expected) {
  276           /* Unfortunately, we must recognise these codes only if we know they
  277            * are known to be valid (i.e., after sending a command), because there
  278            * are some brain-damaged keyboards (yes, FOCUS 9000 again) which have
  279            * keys with such codes :(
  280            */
  281                 if (scancode == KBD_REPLY_ACK) {
  282                         acknowledge = 1;
  283                         reply_expected = 0;
  284                         return 0;
  285                 } else if (scancode == KBD_REPLY_RESEND) {
  286                         resend = 1;
  287                         reply_expected = 0;
  288                         return 0;
  289                 }
  290                 /* Should not happen... */
  291 #if 0
  292                 printk(KERN_DEBUG "keyboard reply expected - got %02x\n",
  293                        scancode);
  294 #endif
  295         }
  296         return 1;
  297 }
  298 
  299 int pckbd_translate(unsigned char scancode, unsigned char *keycode,
  300                     char raw_mode)
  301 {
  302         static int prev_scancode;
  303 
  304         /* special prefix scancodes.. */
  305         if (scancode == 0xe0 || scancode == 0xe1) {
  306                 prev_scancode = scancode;
  307                 return 0;
  308         }
  309 
  310         /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */
  311         if (scancode == 0x00 || scancode == 0xff) {
  312                 prev_scancode = 0;
  313                 return 0;
  314         }
  315 
  316         scancode &= 0x7f;
  317 
  318         if (prev_scancode) {
  319           /*
  320            * usually it will be 0xe0, but a Pause key generates
  321            * e1 1d 45 e1 9d c5 when pressed, and nothing when released
  322            */
  323           if (prev_scancode != 0xe0) {
  324               if (prev_scancode == 0xe1 && scancode == 0x1d) {
  325                   prev_scancode = 0x100;
  326                   return 0;
  327               } else if (prev_scancode == 0x100 && scancode == 0x45) {
  328                   *keycode = E1_PAUSE;
  329                   prev_scancode = 0;
  330               } else {
  331 #ifdef KBD_REPORT_UNKN
  332                   if (!raw_mode)
  333                     printk(KERN_INFO "keyboard: unknown e1 escape sequence\n");
  334 #endif
  335                   prev_scancode = 0;
  336                   return 0;
  337               }
  338           } else {
  339               prev_scancode = 0;
  340               /*
  341                *  The keyboard maintains its own internal caps lock and
  342                *  num lock statuses. In caps lock mode E0 AA precedes make
  343                *  code and E0 2A follows break code. In num lock mode,
  344                *  E0 2A precedes make code and E0 AA follows break code.
  345                *  We do our own book-keeping, so we will just ignore these.
  346                */
  347               /*
  348                *  For my keyboard there is no caps lock mode, but there are
  349                *  both Shift-L and Shift-R modes. The former mode generates
  350                *  E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs.
  351                *  So, we should also ignore the latter. - aeb@cwi.nl
  352                */
  353               if (scancode == 0x2a || scancode == 0x36)
  354                 return 0;
  355 
  356               if (e0_keys[scancode])
  357                 *keycode = e0_keys[scancode];
  358               else {
  359 #ifdef KBD_REPORT_UNKN
  360                   if (!raw_mode)
  361                     printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n",
  362                            scancode);
  363 #endif
  364                   return 0;
  365               }
  366           }
  367         } else if (scancode >= SC_LIM) {
  368             /* This happens with the FOCUS 9000 keyboard
  369                Its keys PF1..PF12 are reported to generate
  370                55 73 77 78 79 7a 7b 7c 74 7e 6d 6f
  371                Moreover, unless repeated, they do not generate
  372                key-down events, so we have to zero up_flag below */
  373             /* Also, Japanese 86/106 keyboards are reported to
  374                generate 0x73 and 0x7d for \ - and \ | respectively. */
  375             /* Also, some Brazilian keyboard is reported to produce
  376                0x73 and 0x7e for \ ? and KP-dot, respectively. */
  377 
  378           *keycode = high_keys[scancode - SC_LIM];
  379 
  380           if (!*keycode) {
  381               if (!raw_mode) {
  382 #ifdef KBD_REPORT_UNKN
  383                   printk(KERN_INFO "keyboard: unrecognized scancode (%02x)"
  384                          " - ignored\n", scancode);
  385 #endif
  386               }
  387               return 0;
  388           }
  389         } else
  390           *keycode = scancode;
  391         return 1;
  392 }
  393 
  394 char pckbd_unexpected_up(unsigned char keycode)
  395 {
  396         /* unexpected, but this can happen: maybe this was a key release for a
  397            FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */
  398         if (keycode >= SC_LIM || keycode == 85)
  399             return 0;
  400         else
  401             return 0200;
  402 }
  403 
  404 int pckbd_pm_resume(struct pm_dev *dev, pm_request_t rqst, void *data) 
  405 {
  406 #if defined CONFIG_PSMOUSE
  407        unsigned long flags;
  408 
  409        if (rqst == PM_RESUME) {
  410                if (queue) {                    /* Aux port detected */
  411                        if (aux_count == 0) {   /* Mouse not in use */ 
  412                                spin_lock_irqsave(&kbd_controller_lock, flags);
  413                                /*
  414                                 * Dell Lat. C600 A06 enables mouse after resume.
  415                                 * When user touches the pad, it posts IRQ 12
  416                                 * (which we do not process), thus holding keyboard.
  417                                 */
  418                                kbd_write_command(KBD_CCMD_MOUSE_DISABLE);
  419                                /* kbd_write_cmd(AUX_INTS_OFF); */ /* Config & lock */
  420                                kb_wait();
  421                                kbd_write_command(KBD_CCMD_WRITE_MODE);
  422                                kb_wait();
  423                                kbd_write_output(AUX_INTS_OFF);
  424                                spin_unlock_irqrestore(&kbd_controller_lock, flags);
  425                        }
  426                }
  427        }
  428 #endif
  429        return 0;
  430 }
  431 
  432 
  433 static inline void handle_mouse_event(unsigned char scancode)
  434 {
  435 #ifdef CONFIG_PSMOUSE
  436         static unsigned char prev_code;
  437         if (mouse_reply_expected) {
  438                 if (scancode == AUX_ACK) {
  439                         mouse_reply_expected--;
  440                         return;
  441                 }
  442                 mouse_reply_expected = 0;
  443         }
  444         else if(scancode == AUX_RECONNECT2 && prev_code == AUX_RECONNECT1
  445                 && aux_reconnect) {
  446                 printk (KERN_INFO "PS/2 mouse reconnect detected\n");
  447                 queue->head = queue->tail = 0;  /* Flush input queue */
  448                 __aux_write_ack(AUX_ENABLE_DEV);  /* ping the mouse :) */
  449                 return;
  450         }
  451 
  452         prev_code = scancode;
  453         add_mouse_randomness(scancode);
  454         if (aux_count) {
  455                 int head = queue->head;
  456 
  457                 queue->buf[head] = scancode;
  458                 head = (head + 1) & (AUX_BUF_SIZE-1);
  459                 if (head != queue->tail) {
  460                         queue->head = head;
  461                         kill_fasync(&queue->fasync, SIGIO, POLL_IN);
  462                         wake_up_interruptible(&queue->proc_list);
  463                 }
  464         }
  465 #endif
  466 }
  467 
  468 static unsigned char kbd_exists = 1;
  469 
  470 static inline void handle_keyboard_event(unsigned char scancode)
  471 {
  472 #ifdef CONFIG_VT
  473         kbd_exists = 1;
  474         if (do_acknowledge(scancode))
  475                 handle_scancode(scancode, !(scancode & 0x80));
  476 #endif                          
  477         tasklet_schedule(&keyboard_tasklet);
  478 }       
  479 
  480 /*
  481  * This reads the keyboard status port, and does the
  482  * appropriate action.
  483  *
  484  * It requires that we hold the keyboard controller
  485  * spinlock.
  486  */
  487 static unsigned char handle_kbd_event(void)
  488 {
  489         unsigned char status = kbd_read_status();
  490         unsigned int work = 10000;
  491 
  492         while ((--work > 0) && (status & KBD_STAT_OBF)) {
  493                 unsigned char scancode;
  494 
  495                 scancode = kbd_read_input();
  496 
  497                 /* Error bytes must be ignored to make the 
  498                    Synaptics touchpads compaq use work */
  499 #if 1
  500                 /* Ignore error bytes */
  501                 if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR)))
  502 #endif
  503                 {
  504                         if (status & KBD_STAT_MOUSE_OBF)
  505                                 handle_mouse_event(scancode);
  506                         else
  507                                 handle_keyboard_event(scancode);
  508                 }
  509 
  510                 status = kbd_read_status();
  511         }
  512                 
  513         if (!work)
  514                 printk(KERN_ERR "pc_keyb: controller jammed (0x%02X).\n", status);
  515 
  516         return status;
  517 }
  518 
  519 
  520 static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  521 {
  522 #ifdef CONFIG_VT
  523         kbd_pt_regs = regs;
  524 #endif
  525 
  526         spin_lock_irq(&kbd_controller_lock);
  527         handle_kbd_event();
  528         spin_unlock_irq(&kbd_controller_lock);
  529 }
  530 
  531 /*
  532  * send_data sends a character to the keyboard and waits
  533  * for an acknowledge, possibly retrying if asked to. Returns
  534  * the success status.
  535  *
  536  * Don't use 'jiffies', so that we don't depend on interrupts
  537  */
  538 static int send_data(unsigned char data)
  539 {
  540         int retries = 3;
  541 
  542         do {
  543                 unsigned long timeout = KBD_TIMEOUT;
  544 
  545                 acknowledge = 0; /* Set by interrupt routine on receipt of ACK. */
  546                 resend = 0;
  547                 reply_expected = 1;
  548                 kbd_write_output_w(data);
  549                 for (;;) {
  550                         if (acknowledge)
  551                                 return 1;
  552                         if (resend)
  553                                 break;
  554                         mdelay(1);
  555                         if (!--timeout) {
  556 #ifdef KBD_REPORT_TIMEOUTS
  557                                 printk(KERN_WARNING "keyboard: Timeout - AT keyboard not present?(%02x)\n", data);
  558 #endif
  559                                 return 0;
  560                         }
  561                 }
  562         } while (retries-- > 0);
  563 #ifdef KBD_REPORT_TIMEOUTS
  564         printk(KERN_WARNING "keyboard: Too many NACKs -- noisy kbd cable?\n");
  565 #endif
  566         return 0;
  567 }
  568 
  569 void pckbd_leds(unsigned char leds)
  570 {
  571         if (kbd_exists && (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))) {
  572                 send_data(KBD_CMD_ENABLE);      /* re-enable kbd if any errors */
  573                 kbd_exists = 0;
  574         }
  575 }
  576 
  577 #define DEFAULT_KEYB_REP_DELAY  250
  578 #define DEFAULT_KEYB_REP_RATE   30      /* cps */
  579 
  580 static struct kbd_repeat kbdrate={
  581         DEFAULT_KEYB_REP_DELAY,
  582         DEFAULT_KEYB_REP_RATE
  583 };
  584 
  585 static unsigned char parse_kbd_rate(struct kbd_repeat *r)
  586 {
  587         static struct r2v{
  588                 int rate;
  589                 unsigned char val;
  590         } kbd_rates[]={ {5,0x14},
  591                         {7,0x10},
  592                         {10,0x0c},
  593                         {15,0x08},
  594                         {20,0x04},
  595                         {25,0x02},
  596                         {30,0x00}
  597         };
  598         static struct d2v{
  599                 int delay;
  600                 unsigned char val;
  601         } kbd_delays[]={{250,0},
  602                         {500,1},
  603                         {750,2},
  604                         {1000,3}
  605         };
  606         int rate=0,delay=0;
  607         if (r != NULL){
  608                 int i,new_rate=30,new_delay=250;
  609                 if (r->rate <= 0)
  610                         r->rate=kbdrate.rate;
  611                 if (r->delay <= 0)
  612                         r->delay=kbdrate.delay;
  613                 for (i=0; i < sizeof(kbd_rates)/sizeof(struct r2v); i++)
  614                         if (kbd_rates[i].rate == r->rate){
  615                                 new_rate=kbd_rates[i].rate;
  616                                 rate=kbd_rates[i].val;
  617                                 break;
  618                         }
  619                 for (i=0; i < sizeof(kbd_delays)/sizeof(struct d2v); i++)
  620                         if (kbd_delays[i].delay == r->delay){
  621                                 new_delay=kbd_delays[i].delay;
  622                                 delay=kbd_delays[i].val;
  623                                 break;
  624                         }
  625                 r->rate=new_rate;
  626                 r->delay=new_delay;
  627         }
  628         return (delay << 5) | rate;
  629 }
  630 
  631 static int write_kbd_rate(unsigned char r)
  632 {
  633         if (!send_data(KBD_CMD_SET_RATE) || !send_data(r)){
  634                 send_data(KBD_CMD_ENABLE);      /* re-enable kbd if any errors */
  635                 return 0;
  636         }else
  637                 return 1;
  638 }
  639 
  640 static int pckbd_rate(struct kbd_repeat *rep)
  641 {
  642         if (rep == NULL)
  643                 return -EINVAL;
  644         else{
  645                 unsigned char r=parse_kbd_rate(rep);
  646                 struct kbd_repeat old_rep;
  647                 memcpy(&old_rep,&kbdrate,sizeof(struct kbd_repeat));
  648                 if (write_kbd_rate(r)){
  649                         memcpy(&kbdrate,rep,sizeof(struct kbd_repeat));
  650                         memcpy(rep,&old_rep,sizeof(struct kbd_repeat));
  651                         return 0;
  652                 }
  653         }
  654         return -EIO;
  655 }
  656 
  657 /*
  658  * In case we run on a non-x86 hardware we need to initialize both the
  659  * keyboard controller and the keyboard.  On a x86, the BIOS will
  660  * already have initialized them.
  661  *
  662  * Some x86 BIOSes do not correctly initialize the keyboard, so the
  663  * "kbd-reset" command line options can be given to force a reset.
  664  * [Ranger]
  665  */
  666 #ifdef __i386__
  667  int kbd_startup_reset __initdata = 0;
  668 #else
  669  int kbd_startup_reset __initdata = 1;
  670 #endif
  671 
  672 /* for "kbd-reset" cmdline param */
  673 static int __init kbd_reset_setup(char *str)
  674 {
  675         kbd_startup_reset = 1;
  676         return 1;
  677 }
  678 
  679 __setup("kbd-reset", kbd_reset_setup);
  680 
  681 #define KBD_NO_DATA     (-1)    /* No data */
  682 #define KBD_BAD_DATA    (-2)    /* Parity or other error */
  683 
  684 static int __init kbd_read_data(void)
  685 {
  686         int retval = KBD_NO_DATA;
  687         unsigned char status;
  688 
  689         status = kbd_read_status();
  690         if (status & KBD_STAT_OBF) {
  691                 unsigned char data = kbd_read_input();
  692 
  693                 retval = data;
  694                 if (status & (KBD_STAT_GTO | KBD_STAT_PERR))
  695                         retval = KBD_BAD_DATA;
  696         }
  697         return retval;
  698 }
  699 
  700 static void __init kbd_clear_input(void)
  701 {
  702         int maxread = 100;      /* Random number */
  703 
  704         do {
  705                 if (kbd_read_data() == KBD_NO_DATA)
  706                         break;
  707         } while (--maxread);
  708 }
  709 
  710 static int __init kbd_wait_for_input(void)
  711 {
  712         long timeout = KBD_INIT_TIMEOUT;
  713 
  714         do {
  715                 int retval = kbd_read_data();
  716                 if (retval >= 0)
  717                         return retval;
  718                 mdelay(1);
  719         } while (--timeout);
  720         return -1;
  721 }
  722 
  723 static void kbd_write_command_w(int data)
  724 {
  725         unsigned long flags;
  726 
  727         spin_lock_irqsave(&kbd_controller_lock, flags);
  728         kb_wait();
  729         kbd_write_command(data);
  730         spin_unlock_irqrestore(&kbd_controller_lock, flags);
  731 }
  732 
  733 static void kbd_write_output_w(int data)
  734 {
  735         unsigned long flags;
  736 
  737         spin_lock_irqsave(&kbd_controller_lock, flags);
  738         kb_wait();
  739         kbd_write_output(data);
  740         spin_unlock_irqrestore(&kbd_controller_lock, flags);
  741 }
  742 
  743 #if defined(__alpha__)
  744 /*
  745  * Some Alphas cannot mask some/all interrupts, so we have to
  746  * make sure not to allow interrupts AT ALL when polling for
  747  * specific return values from the keyboard.
  748  *
  749  * I think this should work on any architecture, but for now, only Alpha.
  750  */
  751 static int kbd_write_command_w_and_wait(int data)
  752 {
  753         unsigned long flags;
  754         int input;
  755 
  756         spin_lock_irqsave(&kbd_controller_lock, flags);
  757         kb_wait();
  758         kbd_write_command(data);
  759         input = kbd_wait_for_input();
  760         spin_unlock_irqrestore(&kbd_controller_lock, flags);
  761         return input;
  762 }
  763 
  764 static int kbd_write_output_w_and_wait(int data)
  765 {
  766         unsigned long flags;
  767         int input;
  768 
  769         spin_lock_irqsave(&kbd_controller_lock, flags);
  770         kb_wait();
  771         kbd_write_output(data);
  772         input = kbd_wait_for_input();
  773         spin_unlock_irqrestore(&kbd_controller_lock, flags);
  774         return input;
  775 }
  776 #else
  777 static int kbd_write_command_w_and_wait(int data)
  778 {
  779         kbd_write_command_w(data);
  780         return kbd_wait_for_input();
  781 }
  782 
  783 static int kbd_write_output_w_and_wait(int data)
  784 {
  785         kbd_write_output_w(data);
  786         return kbd_wait_for_input();
  787 }
  788 #endif /* __alpha__ */
  789 
  790 #if defined CONFIG_PSMOUSE
  791 static void kbd_write_cmd(int cmd)
  792 {
  793         unsigned long flags;
  794 
  795         spin_lock_irqsave(&kbd_controller_lock, flags);
  796         kb_wait();
  797         kbd_write_command(KBD_CCMD_WRITE_MODE);
  798         kb_wait();
  799         kbd_write_output(cmd);
  800         spin_unlock_irqrestore(&kbd_controller_lock, flags);
  801 }
  802 #endif /* CONFIG_PSMOUSE */
  803 
  804 static char * __init initialize_kbd(void)
  805 {
  806         int status;
  807 
  808         /*
  809          * Test the keyboard interface.
  810          * This seems to be the only way to get it going.
  811          * If the test is successful a x55 is placed in the input buffer.
  812          */
  813         kbd_write_command_w(KBD_CCMD_SELF_TEST);
  814         if (kbd_wait_for_input() != 0x55)
  815                 return "Keyboard failed self test";
  816 
  817         /*
  818          * Perform a keyboard interface test.  This causes the controller
  819          * to test the keyboard clock and data lines.  The results of the
  820          * test are placed in the input buffer.
  821          */
  822         kbd_write_command_w(KBD_CCMD_KBD_TEST);
  823         if (kbd_wait_for_input() != 0x00)
  824                 return "Keyboard interface failed self test";
  825 
  826         /*
  827          * Enable the keyboard by allowing the keyboard clock to run.
  828          */
  829         kbd_write_command_w(KBD_CCMD_KBD_ENABLE);
  830 
  831         /*
  832          * Reset keyboard. If the read times out
  833          * then the assumption is that no keyboard is
  834          * plugged into the machine.
  835          * This defaults the keyboard to scan-code set 2.
  836          *
  837          * Set up to try again if the keyboard asks for RESEND.
  838          */
  839         do {
  840                 kbd_write_output_w(KBD_CMD_RESET);
  841                 status = kbd_wait_for_input();
  842                 if (status == KBD_REPLY_ACK)
  843                         break;
  844                 if (status != KBD_REPLY_RESEND)
  845                         return "Keyboard reset failed, no ACK";
  846         } while (1);
  847 
  848         if (kbd_wait_for_input() != KBD_REPLY_POR)
  849                 return "Keyboard reset failed, no POR";
  850 
  851         /*
  852          * Set keyboard controller mode. During this, the keyboard should be
  853          * in the disabled state.
  854          *
  855          * Set up to try again if the keyboard asks for RESEND.
  856          */
  857         do {
  858                 kbd_write_output_w(KBD_CMD_DISABLE);
  859                 status = kbd_wait_for_input();
  860                 if (status == KBD_REPLY_ACK)
  861                         break;
  862                 if (status != KBD_REPLY_RESEND)
  863                         return "Disable keyboard: no ACK";
  864         } while (1);
  865 
  866         kbd_write_command_w(KBD_CCMD_WRITE_MODE);
  867         kbd_write_output_w(KBD_MODE_KBD_INT
  868                               | KBD_MODE_SYS
  869                               | KBD_MODE_DISABLE_MOUSE
  870                               | KBD_MODE_KCC);
  871 
  872         /* ibm powerpc portables need this to use scan-code set 1 -- Cort */
  873         if (!(kbd_write_command_w_and_wait(KBD_CCMD_READ_MODE) & KBD_MODE_KCC))
  874         {
  875                 /*
  876                  * If the controller does not support conversion,
  877                  * Set the keyboard to scan-code set 1.
  878                  */
  879                 kbd_write_output_w(0xF0);
  880                 kbd_wait_for_input();
  881                 kbd_write_output_w(0x01);
  882                 kbd_wait_for_input();
  883         }
  884 
  885         if (kbd_write_output_w_and_wait(KBD_CMD_ENABLE) != KBD_REPLY_ACK)
  886                 return "Enable keyboard: no ACK";
  887 
  888         /*
  889          * Finally, set the typematic rate to maximum.
  890          */
  891         if (kbd_write_output_w_and_wait(KBD_CMD_SET_RATE) != KBD_REPLY_ACK)
  892                 return "Set rate: no ACK";
  893         if (kbd_write_output_w_and_wait(0x00) != KBD_REPLY_ACK)
  894                 return "Set rate: no 2nd ACK";
  895 
  896         return NULL;
  897 }
  898 
  899 void __init pckbd_init_hw(void)
  900 {
  901         if (!kbd_controller_present()) {
  902                 kbd_exists = 0;
  903                 return;
  904         }
  905 
  906         kbd_request_region();
  907 
  908         /* Flush any pending input. */
  909         kbd_clear_input();
  910 
  911         if (kbd_startup_reset) {
  912                 char *msg = initialize_kbd();
  913                 if (msg)
  914                         printk(KERN_WARNING "initialize_kbd: %s\n", msg);
  915         }
  916 
  917 #if defined CONFIG_PSMOUSE
  918         psaux_init();
  919 #endif
  920 
  921         kbd_rate = pckbd_rate;
  922 
  923         /* Ok, finally allocate the IRQ, and off we go.. */
  924         kbd_request_irq(keyboard_interrupt);
  925 }
  926 
  927 #if defined CONFIG_PSMOUSE
  928 
  929 static int __init aux_reconnect_setup (char *str)
  930 {
  931         aux_reconnect = 1;
  932         return 1;
  933 }
  934 
  935 __setup("psaux-reconnect", aux_reconnect_setup);
  936 
  937 /*
  938  * Check if this is a dual port controller.
  939  */
  940 static int __init detect_auxiliary_port(void)
  941 {
  942         unsigned long flags;
  943         int loops = 10;
  944         int retval = 0;
  945 
  946         /* Check if the BIOS detected a device on the auxiliary port. */
  947         if (aux_device_present == 0xaa)
  948                 return 1;
  949 
  950         spin_lock_irqsave(&kbd_controller_lock, flags);
  951 
  952         /* Put the value 0x5A in the output buffer using the "Write
  953          * Auxiliary Device Output Buffer" command (0xD3). Poll the
  954          * Status Register for a while to see if the value really
  955          * turns up in the Data Register. If the KBD_STAT_MOUSE_OBF
  956          * bit is also set to 1 in the Status Register, we assume this
  957          * controller has an Auxiliary Port (a.k.a. Mouse Port).
  958          */
  959         kb_wait();
  960         kbd_write_command(KBD_CCMD_WRITE_AUX_OBUF);
  961 
  962         kb_wait();
  963         kbd_write_output(0x5a); /* 0x5a is a random dummy value. */
  964 
  965         do {
  966                 unsigned char status = kbd_read_status();
  967 
  968                 if (status & KBD_STAT_OBF) {
  969                         (void) kbd_read_input();
  970                         if (status & KBD_STAT_MOUSE_OBF) {
  971                                 printk(KERN_INFO "Detected PS/2 Mouse Port.\n");
  972                                 retval = 1;
  973                         }
  974                         break;
  975                 }
  976                 mdelay(1);
  977         } while (--loops);
  978         spin_unlock_irqrestore(&kbd_controller_lock, flags);
  979 
  980         return retval;
  981 }
  982 
  983 /*
  984  * Send a byte to the mouse.
  985  */
  986 static void aux_write_dev(int val)
  987 {
  988         unsigned long flags;
  989 
  990         spin_lock_irqsave(&kbd_controller_lock, flags);
  991         kb_wait();
  992         kbd_write_command(KBD_CCMD_WRITE_MOUSE);
  993         kb_wait();
  994         kbd_write_output(val);
  995         spin_unlock_irqrestore(&kbd_controller_lock, flags);
  996 }
  997 
  998 /*
  999  * Send a byte to the mouse & handle returned ack
 1000  */
 1001 static void __aux_write_ack(int val)
 1002 {
 1003         kb_wait();
 1004         kbd_write_command(KBD_CCMD_WRITE_MOUSE);
 1005         kb_wait();
 1006         kbd_write_output(val);
 1007         /* we expect an ACK in response. */
 1008         mouse_reply_expected++;
 1009         kb_wait();
 1010 }
 1011 
 1012 static void aux_write_ack(int val)
 1013 {
 1014         unsigned long flags;
 1015 
 1016         spin_lock_irqsave(&kbd_controller_lock, flags);
 1017         __aux_write_ack(val);
 1018         spin_unlock_irqrestore(&kbd_controller_lock, flags);
 1019 }
 1020 
 1021 static unsigned char get_from_queue(void)
 1022 {
 1023         unsigned char result;
 1024         unsigned long flags;
 1025 
 1026         spin_lock_irqsave(&kbd_controller_lock, flags);
 1027         result = queue->buf[queue->tail];
 1028         queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
 1029         spin_unlock_irqrestore(&kbd_controller_lock, flags);
 1030         return result;
 1031 }
 1032 
 1033 
 1034 static inline int queue_empty(void)
 1035 {
 1036         return queue->head == queue->tail;
 1037 }
 1038 
 1039 static int fasync_aux(int fd, struct file *filp, int on)
 1040 {
 1041         int retval;
 1042 
 1043         retval = fasync_helper(fd, filp, on, &queue->fasync);
 1044         if (retval < 0)
 1045                 return retval;
 1046         return 0;
 1047 }
 1048 
 1049 
 1050 /*
 1051  * Random magic cookie for the aux device
 1052  */
 1053 #define AUX_DEV ((void *)queue)
 1054 
 1055 static int release_aux(struct inode * inode, struct file * file)
 1056 {
 1057         lock_kernel();
 1058         fasync_aux(-1, file, 0);
 1059         if (--aux_count) {
 1060                 unlock_kernel();
 1061                 return 0;
 1062         }
 1063         kbd_write_cmd(AUX_INTS_OFF);                        /* Disable controller ints */
 1064         kbd_write_command_w(KBD_CCMD_MOUSE_DISABLE);
 1065         aux_free_irq(AUX_DEV);
 1066         unlock_kernel();
 1067         return 0;
 1068 }
 1069 
 1070 /*
 1071  * Install interrupt handler.
 1072  * Enable auxiliary device.
 1073  */
 1074 
 1075 static int open_aux(struct inode * inode, struct file * file)
 1076 {
 1077         if (aux_count++) {
 1078                 return 0;
 1079         }
 1080         queue->head = queue->tail = 0;          /* Flush input queue */
 1081         if (aux_request_irq(keyboard_interrupt, AUX_DEV)) {
 1082                 aux_count--;
 1083                 return -EBUSY;
 1084         }
 1085         kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE);     /* Enable the
 1086                                                            auxiliary port on
 1087                                                            controller. */
 1088         aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */
 1089         kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */
 1090         
 1091         mdelay(2);                      /* Ensure we follow the kbc access delay rules.. */
 1092 
 1093         send_data(KBD_CMD_ENABLE);      /* try to workaround toshiba4030cdt problem */
 1094 
 1095         return 0;
 1096 }
 1097 
 1098 /*
 1099  * Put bytes from input queue to buffer.
 1100  */
 1101 
 1102 static ssize_t read_aux(struct file * file, char * buffer,
 1103                         size_t count, loff_t *ppos)
 1104 {
 1105         DECLARE_WAITQUEUE(wait, current);
 1106         ssize_t i = count;
 1107         unsigned char c;
 1108 
 1109         if (queue_empty()) {
 1110                 if (file->f_flags & O_NONBLOCK)
 1111                         return -EAGAIN;
 1112                 add_wait_queue(&queue->proc_list, &wait);
 1113 repeat:
 1114                 set_current_state(TASK_INTERRUPTIBLE);
 1115                 if (queue_empty() && !signal_pending(current)) {
 1116                         schedule();
 1117                         goto repeat;
 1118                 }
 1119                 current->state = TASK_RUNNING;
 1120                 remove_wait_queue(&queue->proc_list, &wait);
 1121         }
 1122         while (i > 0 && !queue_empty()) {
 1123                 c = get_from_queue();
 1124                 put_user(c, buffer++);
 1125                 i--;
 1126         }
 1127         if (count-i) {
 1128                 file->f_dentry->d_inode->i_atime = CURRENT_TIME;
 1129                 return count-i;
 1130         }
 1131         if (signal_pending(current))
 1132                 return -ERESTARTSYS;
 1133         return 0;
 1134 }
 1135 
 1136 /*
 1137  * Write to the aux device.
 1138  */
 1139 
 1140 static ssize_t write_aux(struct file * file, const char * buffer,
 1141                          size_t count, loff_t *ppos)
 1142 {
 1143         ssize_t retval = 0;
 1144 
 1145         if (count) {
 1146                 ssize_t written = 0;
 1147 
 1148                 if (count > 32)
 1149                         count = 32; /* Limit to 32 bytes. */
 1150                 do {
 1151                         char c;
 1152                         get_user(c, buffer++);
 1153                         aux_write_dev(c);
 1154                         written++;
 1155                 } while (--count);
 1156                 retval = -EIO;
 1157                 if (written) {
 1158                         retval = written;
 1159                         file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
 1160                 }
 1161         }
 1162 
 1163         return retval;
 1164 }
 1165 
 1166 /* No kernel lock held - fine */
 1167 static unsigned int aux_poll(struct file *file, poll_table * wait)
 1168 {
 1169         poll_wait(file, &queue->proc_list, wait);
 1170         if (!queue_empty())
 1171                 return POLLIN | POLLRDNORM;
 1172         return 0;
 1173 }
 1174 
 1175 struct file_operations psaux_fops = {
 1176         read:           read_aux,
 1177         write:          write_aux,
 1178         poll:           aux_poll,
 1179         open:           open_aux,
 1180         release:        release_aux,
 1181         fasync:         fasync_aux,
 1182 };
 1183 
 1184 /*
 1185  * Initialize driver.
 1186  */
 1187 static struct miscdevice psaux_mouse = {
 1188         PSMOUSE_MINOR, "psaux", &psaux_fops
 1189 };
 1190 
 1191 static int __init psaux_init(void)
 1192 {
 1193         int retval;
 1194 
 1195         if (!detect_auxiliary_port())
 1196                 return -EIO;
 1197 
 1198         if ((retval = misc_register(&psaux_mouse)))
 1199                 return retval;
 1200 
 1201         queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
 1202         if (queue == NULL) {
 1203                 printk(KERN_ERR "psaux_init(): out of memory\n");
 1204                 misc_deregister(&psaux_mouse);
 1205                 return -ENOMEM;
 1206         }
 1207         memset(queue, 0, sizeof(*queue));
 1208         queue->head = queue->tail = 0;
 1209         init_waitqueue_head(&queue->proc_list);
 1210 
 1211 #ifdef INITIALIZE_MOUSE
 1212         kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable Aux. */
 1213         aux_write_ack(AUX_SET_SAMPLE);
 1214         aux_write_ack(100);                     /* 100 samples/sec */
 1215         aux_write_ack(AUX_SET_RES);
 1216         aux_write_ack(3);                       /* 8 counts per mm */
 1217         aux_write_ack(AUX_SET_SCALE21);         /* 2:1 scaling */
 1218 #endif /* INITIALIZE_MOUSE */
 1219         kbd_write_command(KBD_CCMD_MOUSE_DISABLE); /* Disable aux device. */
 1220         kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints. */
 1221 
 1222         return 0;
 1223 }
 1224 
 1225 #endif /* CONFIG_PSMOUSE */
 1226 
 1227 
 1228 static int blink_frequency = HZ/2;
 1229 
 1230 /* Tell the user who may be running in X and not see the console that we have 
 1231    panic'ed. This is to distingush panics from "real" lockups. 
 1232    Could in theory send the panic message as morse, but that is left as an
 1233    exercise for the reader.  */ 
 1234 void panic_blink(void)
 1235 { 
 1236         static unsigned long last_jiffie;
 1237         static char led;
 1238         /* Roughly 1/2s frequency. KDB uses about 1s. Make sure it is 
 1239            different. */
 1240         if (!blink_frequency) 
 1241                 return;
 1242         if (jiffies - last_jiffie > blink_frequency) {
 1243                 led ^= 0x01 | 0x04;
 1244                 while (kbd_read_status() & KBD_STAT_IBF) mdelay(1); 
 1245                 kbd_write_output(KBD_CMD_SET_LEDS);
 1246                 mdelay(1); 
 1247                 while (kbd_read_status() & KBD_STAT_IBF) mdelay(1); 
 1248                 mdelay(1); 
 1249                 kbd_write_output(led);
 1250                 last_jiffie = jiffies;
 1251         }
 1252 }  
 1253 
 1254 static int __init panicblink_setup(char *str)
 1255 {
 1256     int par;
 1257     if (get_option(&str,&par)) 
 1258             blink_frequency = par*(1000/HZ);
 1259     return 1;
 1260 }
 1261 
 1262 /* panicblink=0 disables the blinking as it caused problems with some console
 1263    switches. otherwise argument is ms of a blink period. */
 1264 __setup("panicblink=", panicblink_setup);
 1265 

Cache object: ee0cdc8d41c51cd6af53708d62229de2


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.