minix/lib/libinputdriver/inputdriver.c
David van Moolenbroek 6b3f4dc157 Input infrastructure, INPUT server, PCKBD driver
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
2014-03-01 09:04:55 +01:00

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);
}
}