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

Implementation of the SIGIO signal handler

As seen in section 5.1 function xf86AddEnabledDevice() is called from MouseProc() (generally speaking the
pInfo->device_control routine
) when called with a DEVICE_ON as the second argument. It is implemented as:

/*
 * xf86AddEnabledDevice --
 *    
 */
void
xf86AddEnabledDevice(InputInfoPtr pInfo)
{
    if (!xf86InstallSIGIOHandler (pInfo->fd, xf86SigioReadInput, pInfo)) {
	AddEnabledDevice(pInfo->fd);
    }
}

The first function called by xf86AddEnabledDevice(), xf86InstallSIGIOHandler(), is implemented as:

int
xf86InstallSIGIOHandler(int fd, void (*f)(int, void *), void *closure)
{
    struct sigaction sa;
    struct sigaction osa;
    int	i;
    int blocked;

    for (i = 0; i < MAX_FUNCS; i++)
    {
	if (!xf86SigIOFuncs[i].f)
	{
	    if (xf86IsPipe (fd))
		return 0;
	    blocked = xf86BlockSIGIO();
	    if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_ASYNC) == -1) {
#ifdef XFree86Server
		xf86Msg(X_WARNING, "fcntl(%d, O_ASYNC): %s\n", 
			fd, strerror(errno));
#else
		fprintf(stderr,"fcntl(%d, O_ASYNC): %s\n", 
			fd, strerror(errno));
#endif
		xf86UnblockSIGIO(blocked);
		return 0;
	    }
	    if (fcntl(fd, F_SETOWN, getpid()) == -1) {
#ifdef XFree86Server
		xf86Msg(X_WARNING, "fcntl(%d, F_SETOWN): %s\n", 
			fd, strerror(errno));
#else
		fprintf(stderr,"fcntl(%d, F_SETOWN): %s\n", 
			fd, strerror(errno));
#endif
		xf86UnblockSIGIO(blocked);
		return 0;
	    }
	    sigemptyset(&sa.sa_mask);
	    sigaddset(&sa.sa_mask, SIGIO);
	    sa.sa_flags   = 0;
	    sa.sa_handler = xf86SIGIO;
	    sigaction(SIGIO, &sa, &osa);
	    xf86SigIOFuncs[i].fd = fd;
	    xf86SigIOFuncs[i].closure = closure;
	    xf86SigIOFuncs[i].f = f;
	    if (i >= xf86SigIOMax)
		xf86SigIOMax = i+1;
	    if (fd >= xf86SigIOMaxFd)
		xf86SigIOMaxFd = fd + 1;
	    FD_SET (fd, &xf86SigIOMask);
	    xf86UnblockSIGIO(blocked);
	    return 1;
	}
 	/* Allow overwriting of the closure and callback */
 	else if (xf86SigIOFuncs[i].fd == fd)
 	{
 	    xf86SigIOFuncs[i].closure = closure;
 	    xf86SigIOFuncs[i].f = f;
 	    return 1;
 	}
    }
    return 0;
}

xf86InstallSIGIOHandler() installs xf86SigioReadInput() as the function that responds to a SIGIO signal. This signal is issued when input is available from a certain input device file descriptor (fd). Normally SIGIO can not be assigned to a specific fd however this is solved through an indirect way:

xf86InstallSIGIOHandler() calls sigaction() to install xf86SIGIO() as the signal handler for SIGIO and this function will do what its comment notes:

 * SIGIO gives no way of discovering which fd signalled, select
 * to discover
Therefore to install a signal handler, for instance function f, for a specific fd via xf86InstallSIGIOHandler() another function, xf86SIGIO(), is assigned as the signal handler and this function represents all others signal handlers set by different fds. It uses the array xf86SigIOFuncs[] to store at the next available element a Xf86SigIOFunc struct, which includes the fd of the device, the specific handler (f) and the pInfo (InputInfoPtr) of the device, referred here as 'closure'. Also the fd in included in the xf86SigIOMask fd set.

When xf86SIGIO() runs it uses the select() system call to find the ready fds from xf86SigIOMask and return its number to xf86SigIOMaxFd. Then it searches in xf86SigIOFuncs[] to locate the array's items with the fds that are included in xf86SigIOMask. For those items the specific handler routine is called as:

 
	    (*xf86SigIOFuncs[i].f)(xf86SigIOFuncs[i].fd,
				   xf86SigIOFuncs[i].closure);
As seen previously from xf86AddEnabledDevice() the handler routine used is xf86SigioReadInput(), which calls the read_input function from the pInfo that owns the specific fd.
   pInfo->read_input(pInfo);

Example

As seen in section 5.1 for the current example read_input is MouseReadInput(). This function is discussed in section 5.1.3.

xf86SigIOFuncs[] is defined in sigio.c as:

static Xf86SigIOFunc	xf86SigIOFuncs[MAX_FUNCS];

Xf86SigIOFunc is defined in the same file as:


typedef struct _xf86SigIOFunc {
    void    (*f) (int, void *);
    int	    fd;
    void    *closure;
} Xf86SigIOFunc;

where MAX_FUNCS is the maximum number of devices and is defined in the same file as:

#ifdef MAX_DEVICES
/* MAX_DEVICES represents the maximimum number of input devices usable
 * at the same time plus one entry for DRM support.
 */
# define MAX_FUNCS   (MAX_DEVICES + 1)
#else
# define MAX_FUNCS 16
#endif

When a SIGIO is received by the X Server xf86SIGIO() runs:

/*
 * SIGIO gives no way of discovering which fd signalled, select
 * to discover
 */
static void
xf86SIGIO (int sig)
{
    int	    i;
    fd_set  ready;
    struct timeval  to;
    int	    r;

    ready = xf86SigIOMask;
    to.tv_sec = 0;
    to.tv_usec = 0;
    SYSCALL (r = select (xf86SigIOMaxFd, &ready, 0, 0, &to));
    for (i = 0; r > 0 && i < xf86SigIOMax; i++)
	if (xf86SigIOFuncs[i].f && FD_ISSET (xf86SigIOFuncs[i].fd, &ready))
	{
	    (*xf86SigIOFuncs[i].f)(xf86SigIOFuncs[i].fd,
				   xf86SigIOFuncs[i].closure);
	    r--;
	}
#ifdef XFree86Server
    if (r > 0) {
      xf86Msg(X_ERROR, "SIGIO %d descriptors not handled\n", r);
    }
#endif
}

As function *xf86SigIOFuncs[i].f was set by xf86InstallSIGIOHandler() the xf86SigioReadInput, which is implemented as:

/*
 * xf86SigioReadInput --
 *    signal handler for the SIGIO signal.
 */
static void
xf86SigioReadInput(int fd,
		   void *closure)
{
    int sigstate = xf86BlockSIGIO();
    InputInfoPtr pInfo = (InputInfoPtr) closure;

    pInfo->read_input(pInfo);

    xf86UnblockSIGIO(sigstate);
}

Example

In the example we follow as pInfo->read_input was set MouseReadInput(), which we examine in section 5.1.3

The second function called from xf86AddEnabledDevice(), AddEnabledDevice(), is implemented as:

void
AddEnabledDevice(int fd)
{
    FD_SET(fd, &EnabledDevices);
    FD_SET(fd, &AllSockets);
    if (GrabInProgress)
	FD_SET(fd, &SavedAllSockets);
}

AddEnabledDevice() adds the input device fd to EnabledDevices (this is how EnabledDevices is filled) and to AllSockets sets. We met those sets at section 3.2.5.2.

REFERENCES

Input Event Processing
Distributed Multihead X design