Site hosted by Angelfire.com: Build your free website today!

Hands-on Projects for the Linux Graphics Subsystem

New book from Christos Karayiannis

Available in Amazon Kindle format at:

amazon.com   amazon.co.uk   amazon.de   amazon.es   amazon.fr   amazon.it

Overview

We start the input driver discussion taking as example the mouse driver, found in mouse.c and used previously in section 3.2.3.

As seen in section 3.2.3 InitInput() the driver's PreInit routine is called as:

	    pInfo = pDrv->PreInit(pDrv, pDev, 0);

The PreInit function for the mouse device is MousePreInit(), discussed in section 3.2.3. Notice that we refer to the mouse module as 'mouse device' to discriminate from the OS specific term 'mouse device driver' that controlls the mouse and will be used next by the current mouse device.

The PreInit routine, as seen in section 3.2.3, fills and returns pInfo (a pointer to an InputInfoRec) with information about the mouse device. MousePreInit calls the following instructions:

    xf86CollectInputOptions(pInfo, pProto->defaults, NULL);
    xf86ProcessCommonOptions(pInfo, pInfo->options);
    .  .  .
    /* Check if the device can be opened. */
    pInfo->fd = xf86OpenSerial(pInfo->options);
    .  .  .
    xf86CloseSerial(pInfo->fd);

xf86CollectInputOptions() collects the input options from pInfo->conf_idev. For the example we followed in section 3.2.3 the options are presented in the conf_idev field of the Mouse0 InputInfoRec (pointed by the example's pInfo).

In this section we are interested mainly for the 'Device' option, which for the example of section 3.2.3 has the value:

Example

"Device" "/dev/input/mice

Notice that the xf86OpenSerial(), xf86CloseSerial() pair is called for testing purposes and the xf86OpenSerial() will be called for good from the mouse 'device_control' function.

device_control is one of the pInfo fields that represent function. We read from XorgInputHOWTO about the pInfo functions:
  • read_input: will be called when new data is available (see section 5.1.2).
  • switch_mode: will be called when a client requests to switch between absolute and relative mode of a device. (Not applicable for our random device).
  • device_control: will be called when the device is switched on or off.

Example

Those functions for the current example were set at MousePreInit(), at section as:

pInfo->read_input = MouseReadInput;
pInfo->switch_mode = NULL;
pInfo->device_control = MouseProc;

MousePreInit() set as device_control function the MouseProc() with the following MousePreInit() instruction:

    pInfo->device_control = MouseProc;

The following xf86ActivateDevice() instruction, called from InitInput():

	dev = AddInputDevice(local->device_control,
			     open_on_init);

As seen in section 3.2.3 _AddInputDevice() called by AddInputDevice() sets the deviceProc of the dev (the InputDriverPtr of the pInfo struct):

   dev->deviceProc = deviceProc;

As deviceProc was passed the device_control, therefore deviceProc is the device_control, in our example MouseProc().

MouseProc()

MouseProc() is called from InitAndStartDevices() , which as seen in section 3.2.4 is called after InitInput() from the X server's main() function as:

	dev->inited = ((*dev->deviceProc)(dev, DEVICE_INIT) == Success);

Then as seen also in section 3.2.4 InitAndStartDevices() calls EnableDevice(), which also calls deviceProc this thime with a second argument DEVICE_ON:

	((*dev->deviceProc)(dev, DEVICE_ON) != Success))

The part of MouseProc() that runs for DEVICE_ON is the following:

    case DEVICE_ON:
	pInfo->fd = xf86OpenSerial(pInfo->options);
	if (pInfo->fd == -1)
	    xf86Msg(X_WARNING, "%s: cannot open input device\n", pInfo->name);
	else {
	    if (pMse->xisbscale)
		pMse->buffer = XisbNew(pInfo->fd, pMse->xisbscale * 4);
	    else
		pMse->buffer = XisbNew(pInfo->fd, 64);
	    if (!pMse->buffer) {
		xf86CloseSerial(pInfo->fd);
		pInfo->fd = -1;
	    } else {
		if (!SetupMouse(pInfo)) {
		    xf86CloseSerial(pInfo->fd);
		    pInfo->fd = -1;
		    XisbFree(pMse->buffer);
		    pMse->buffer = NULL;
		} else {
		    mPriv = (mousePrivPtr)pMse->mousePriv;
		    if (mPriv != NULL) {
			if ( pMse->protocolID != PROT_AUTO) {
			    pMse->inSync = TRUE; /* @@@ */
			    if (mPriv->soft)
				mPriv->autoState = AUTOPROBE_GOOD;
			    else
				mPriv->autoState = AUTOPROBE_H_GOOD;
			} else {
			    if (mPriv->soft)
				mPriv->autoState = AUTOPROBE_NOPROTO;
			    else
				mPriv->autoState = AUTOPROBE_H_NOPROTO;
			}
		    }
		    xf86FlushInput(pInfo->fd);
		    xf86AddEnabledDevice(pInfo);
		}
	    }
	}
	pMse->lastButtons = 0;
	pMse->lastMappedButtons = 0;
	pMse->emulateState = 0;
	pMse->emulate3Pending = FALSE;
	pMse->wheelButtonExpires = GetTimeInMillis ();
	device->public.on = TRUE;
	FlushButtons(pMse);
	if (pMse->emulate3Buttons || pMse->emulate3ButtonsSoft)
	{
	    RegisterBlockAndWakeupHandlers (MouseBlockHandler, MouseWakeupHandler,
					    (pointer) pInfo);
	}
	break;

At this part of MouseProc() xf86OpenSerial() is called, which as mentioned previously is the routine that opens the OS mouse device driver.

xf86OpenSerial()

xf86OpenSerial() is implemented in posix_tty.c. This function finds the device to open by calling

	dev = xf86SetStrOption (options, "Device", NULL);

xf86SetStrOption() calls LookupStrOption() and this ParseOptionValue() to obtain the device name from the pInfo options.

xf86OpenSerial() calls then the open() syscall as:

	SYSCALL (fd = open (dev, O_RDWR | O_NONBLOCK));

The file descriptor (fd) of the device driver is returned by open() and this is also the return value of xf86OpenSerial().

Example

For the current example the device name would be the pInfo.name that is "Mouse0". Also LookupStrOption() would search for the option "Device", which returns "/dev/input/mice".

The open() system call therefore would be issued as:

fd = open ("/dev/input/mice", O_RDWR | O_NONBLOCK);

SetupMouse()

SetupMouse() is covered in section 5.1.1

xf86FlushInput()

xf86FlushInput() clears any pending input. It is implemented as:

int
xf86FlushInput(int fd)
{
	fd_set fds;
	struct timeval timeout;
	char c[4];

#ifdef DEBUG
	ErrorF("FlushingSerial\n");
#endif
	if (tcflush(fd, TCIFLUSH) == 0)
		return 0;

	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
	FD_ZERO(&fds);
	FD_SET(fd, &fds);
	while (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
		if (read(fd, &c, sizeof(c)) < 1)
		    return 0;
		FD_ZERO(&fds);
		FD_SET(fd, &fds);
	}
	return 0;
}

xf86FlushInput() either calls tcflush() to flush any input not read yet or uses the read() system call to empty the input 'manually'. In the latter case the select() with a timeout 0 (return immediately) is used to examine if any input is pending for the current fd. After the select() the fds set (the set fd was formed by itself) is zeroed since after a successful select() the fds is changed. Then it continues with other read() via the 'while' loop until the input is drained.

xf86FlushInput() is required before a xf86AddEnabledDevice() call to clear any garbage, if any, before the current fd is used to accept mouse data via the SIGIO signal. For instance via SetupMouse() that was called previously as seen in section 5.1.1 a initMouseHW() call could be issued, which in many cases call a xf86WriteSerial().

xf86AddEnabledDevice()

As seen more detailed in section 5.1.2 function xf86AddEnabledDevice() is called to add the fd to the ones monitored by the event loop. It calls xf86InstallSIGIOHandler() which installs the signal handler via sigaddset(), sigaction() system calls. The installed signal is SIGIO, which is mainly generated when new data arrive at the socket.

When the server receives the SIGIO signal the specific signal handler function is selected (see section 5.1.2) and such a function MouseReadInput() (as seen in section 5.1.3) is examined. MouseReadInput() calls finally MousePostEvent, described in section 5.1.4, to deliver the mouse events to the specific client.

REFERENCES

XOrgInputDriverSpec
XorgInputHOWTO
Distributed Multihead X design