This commit separates the low-level keyboard driver from TTY, putting it in a separate driver (PCKBD). The commit also separates management of raw input devices from TTY, and puts it in a separate server (INPUT). All keyboard and mouse input from hardware is sent by drivers to the INPUT server, which either sends it to a process that has opened a raw input device, or otherwise forwards it to TTY for standard processing. Design by Dirk Vogt. Prototype by Uli Kastlunger. Additional changes made to the prototype: - the event communication is now based on USB HID codes; all input drivers have to use USB codes to describe events; - all TTY keymaps have been converted to USB format, with the effect that a single keymap covers all keys; there is no (static) escaped keymap anymore; - further keymap tweaks now allow remapping of literally all keys; - input device renumbering and protocol rewrite; - INPUT server rewrite, with added support for cancel and select; - PCKBD reimplementation, including PC/AT-to-USB translation; - support for manipulating keyboard LEDs has been added; - keyboard and mouse multiplexer devices have been added to INPUT, primarily so that an X server need only open two devices; - a new "libinputdriver" library abstracts away protocol details from input drivers, and should be used by all future input drivers; - both INPUT and PCKBD can be restarted; - TTY is now scheduled by KERNEL, so that it won't be punished for running a lot; without this, simply running "yes" on the console kills the system; - the KIOCBELL IOCTL has been moved to /dev/console; - support for the SCANCODES termios setting has been removed; - obsolete keymap compression has been removed; - the obsolete Olivetti M24 keymap has been removed. Change-Id: I3a672fb8c4fd566734e4b46d3994b4b7fc96d578
206 lines
4.7 KiB
C
206 lines
4.7 KiB
C
/* This file contains the device independent input driver interface. */
|
|
/*
|
|
* Changes:
|
|
* Sep 22, 2013 created (D.C. van Moolenbroek)
|
|
*/
|
|
|
|
#include <minix/drivers.h>
|
|
#include <minix/inputdriver.h>
|
|
#include <minix/ds.h>
|
|
|
|
static endpoint_t input_endpt = NONE;
|
|
static int kbd_id = INVALID_INPUT_ID;
|
|
static int mouse_id = INVALID_INPUT_ID;
|
|
|
|
static int running;
|
|
|
|
/*
|
|
* Announce that we are up after a fresh start or restart.
|
|
*/
|
|
void
|
|
inputdriver_announce(unsigned int type)
|
|
{
|
|
const char *driver_prefix = "drv.inp.";
|
|
char key[DS_MAX_KEYLEN];
|
|
char label[DS_MAX_KEYLEN];
|
|
int r;
|
|
|
|
/* Publish a driver up event. */
|
|
if ((r = ds_retrieve_label_name(label, getprocnr())) != OK)
|
|
panic("libinputdriver: unable to retrieve own label: %d", r);
|
|
|
|
snprintf(key, sizeof(key), "%s%s", driver_prefix, label);
|
|
if ((r = ds_publish_u32(key, type, DSF_OVERWRITE)) != OK)
|
|
panic("libinputdriver: unable to publish up event: %d", r);
|
|
|
|
/* Now we wait for the input server to contact us. */
|
|
}
|
|
|
|
/*
|
|
* Send an event to the input server.
|
|
*/
|
|
void
|
|
inputdriver_send_event(int mouse, unsigned short page, unsigned short code,
|
|
int value, int flags)
|
|
{
|
|
message m;
|
|
int id;
|
|
|
|
if (input_endpt == NONE)
|
|
return;
|
|
|
|
id = mouse ? mouse_id : kbd_id;
|
|
if (id == INVALID_INPUT_ID)
|
|
return;
|
|
|
|
memset(&m, 0, sizeof(m));
|
|
|
|
m.m_type = INPUT_EVENT;
|
|
m.INPUT_ID = id;
|
|
m.INPUT_PAGE = page;
|
|
m.INPUT_CODE = code;
|
|
m.INPUT_VALUE = value;
|
|
m.INPUT_FLAGS = flags;
|
|
|
|
/*
|
|
* Use a blocking send call, for two reasons. First, this avoids the
|
|
* situation that we ever end up queuing too many asynchronous messages
|
|
* to the input server. Second, it allows us to detect trivially if
|
|
* the input server has crashed, in which case we should stop sending
|
|
* more messages to it.
|
|
*/
|
|
if (send(input_endpt, &m) != OK)
|
|
input_endpt = NONE;
|
|
}
|
|
|
|
/*
|
|
* The input server requests that we configure the driver. This request should
|
|
* be sent to us once, although it may be sent multiple times if the input
|
|
* server crashes and recovers. The configuration consists of device IDs for
|
|
* use in keyboard and/or mouse events, one per each device type.
|
|
*/
|
|
static void
|
|
do_conf(message *m_ptr)
|
|
{
|
|
endpoint_t ep;
|
|
int r;
|
|
|
|
/* Make sure that the sender is actually the input server. */
|
|
if ((r = ds_retrieve_label_endpt("input", &ep)) != OK) {
|
|
printf("libinputdriver: unable to get input endpoint (%d)\n",
|
|
r);
|
|
|
|
return; /* ignore message */
|
|
}
|
|
|
|
if (ep != m_ptr->m_source) {
|
|
printf("libinputdriver: ignoring CONF request from %u\n",
|
|
m_ptr->m_source);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Save the new state. */
|
|
input_endpt = m_ptr->m_source;
|
|
kbd_id = m_ptr->INPUT_KBD_ID;
|
|
mouse_id = m_ptr->INPUT_MOUSE_ID;
|
|
|
|
/* If the input server is "full" there's nothing for us to do. */
|
|
if (kbd_id == INVALID_INPUT_ID && mouse_id == INVALID_INPUT_ID)
|
|
printf("libinputdriver: no IDs given, driver disabled\n");
|
|
}
|
|
|
|
/*
|
|
* The input server is telling us to change the LEDs to a particular mask.
|
|
* For now this is for keyboards only, so no device type is provided.
|
|
* This approach was chosen over sending toggle events for the individual LEDs
|
|
* for convenience reasons only.
|
|
*/
|
|
static void
|
|
do_setleds(struct inputdriver *idp, message *m_ptr)
|
|
{
|
|
unsigned int mask;
|
|
|
|
if (m_ptr->m_source != input_endpt) {
|
|
printf("libinputdriver: ignoring SETLEDS request from %u\n",
|
|
m_ptr->m_source);
|
|
|
|
return;
|
|
}
|
|
|
|
mask = m_ptr->INPUT_LED_MASK;
|
|
|
|
if (idp->idr_leds)
|
|
idp->idr_leds(mask);
|
|
}
|
|
|
|
/*
|
|
* Call the appropriate driver function, based on the type of message.
|
|
* All messages in the input protocol are one-way, so we never send a reply.
|
|
*/
|
|
void
|
|
inputdriver_process(struct inputdriver *idp, message *m_ptr, int ipc_status)
|
|
{
|
|
/* Check for notifications first. */
|
|
if (is_ipc_notify(ipc_status)) {
|
|
switch (_ENDPOINT_P(m_ptr->m_source)) {
|
|
case HARDWARE:
|
|
if (idp->idr_intr)
|
|
idp->idr_intr(m_ptr->NOTIFY_ARG);
|
|
break;
|
|
|
|
case CLOCK:
|
|
if (idp->idr_alarm)
|
|
idp->idr_alarm(m_ptr->NOTIFY_TIMESTAMP);
|
|
break;
|
|
|
|
default:
|
|
if (idp->idr_other)
|
|
idp->idr_other(m_ptr, ipc_status);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
switch (m_ptr->m_type) {
|
|
case INPUT_CONF: do_conf(m_ptr); break;
|
|
case INPUT_SETLEDS: do_setleds(idp, m_ptr); break;
|
|
default:
|
|
if (idp->idr_other)
|
|
idp->idr_other(m_ptr, ipc_status);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Break out of the main loop after finishing the current request.
|
|
*/
|
|
void
|
|
inputdriver_terminate(void)
|
|
{
|
|
running = FALSE;
|
|
|
|
sef_cancel();
|
|
}
|
|
|
|
/*
|
|
* Main program of any input driver task.
|
|
*/
|
|
void
|
|
inputdriver_task(struct inputdriver *idp)
|
|
{
|
|
message m;
|
|
int r, ipc_status;
|
|
|
|
running = TRUE;
|
|
|
|
while (running) {
|
|
if ((r = sef_receive_status(ANY, &m, &ipc_status)) != OK) {
|
|
if (r == EINTR && !running)
|
|
break;
|
|
|
|
panic("libinputdriver: receive failed: %d", r);
|
|
}
|
|
|
|
inputdriver_process(idp, &m, ipc_status);
|
|
}
|
|
}
|