using two keyboards at the same time

Norbert Koch NKoch at demig.de
Thu Dec 16 00:53:24 PST 2004


Hello.

I know, the syscons driver does not allow to have
two keyboards attached at the same time.

So my idea was to have a userland application
which polls the keyboard(s) currently _not_
attached to syscons using select(2) or poll(2)
and then switching to an active keyboard.
>From reading the source code under /sys/dev/kbd
I thought this should work.

I made this simple test: I attached syscons to
/dev/kbd1 and ran "cat /dev/kbd0".
As expected I saw characters
coming from both keyboards.

Then I wrote a program to do the selecting and switching.
Well, it does not work (under FreeBSD 4.10).
Select never returns. As I understand
it a device driver should return immediately
if it does not support poll.

Does anyone have an idea?
May be I just made some stupid mistake.

Thank you.


>--------------------------------------------


#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <machine/console.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>


#define MAX_DEVICES     5
#define TEST

#define STR(x)  #x
#define XSTR(x) STR (x)


static void usage (void)
{
  fprintf (stderr,
           "usage: autosw [ -D ]  [ -d ]  [ [ -f <device>] ... ]\n"
           "  -D : do not detach\n"
           "  -d : enable debug output\n"
           "  -f : specify keyboard device (up to " XSTR (MAX_DEVICES) ")\n"
           "       if no keyboard devices specified\n"
           "       /dev/kbd0 and /dev/kbd1 are polled\n");
  exit (1);
}


/*
 * borrowed from /usr/src/usr.sbin/kbdcontrol/kbdcontrol.c
 */
static int set_keyboard_fd (int fd)
{
  keyboard_info_t info;

  if (ioctl (fd, KDGKBINFO, & info) == -1)
  {
    close (fd);
    return -1;
  };
  ioctl (fd, CONS_RELKBD, 0);
  close (fd);
  if (ioctl (0, CONS_SETKBD, info.kb_index) == -1)
  {
    return -1;
  };
  return 0;
}


static int set_keyboard (char * device)
{
  int fd;

  fd = open (device, O_RDONLY);
  if (fd < 0)
  {
    return -1;
  };
  return set_keyboard_fd (fd);
}


int main (int argc, char ** argv)
{
  char * devices[MAX_DEVICES];
  fd_set ifds, ofds;
  int ch, i, fd, maxfd, no_devices = 0, debug = 0, detach = 1, detached = 0;
  struct stat sb;

  while ((ch = getopt (argc, argv, "Ddf:h")) != -1)
  {
    switch (ch)
    {
      case 'D':
        detach = 0;
        break;

      case 'd':
        ++ debug;
        break;

      case 'f':
        if (no_devices >= MAX_DEVICES)
        {
          fprintf (stderr, "too many devices\n");
          exit (1);
        };
        if (stat (optarg, & sb) < 0)
        {
          fprintf (stderr, "cannot stat %s: %s\n", optarg, strerror
(errno));
          exit (1);
        };
        if ((sb.st_mode & S_IFCHR) == 0)
        {
          fprintf (stderr, "not a character device: %s\n", optarg);
          exit (1);
        };
        devices[no_devices ++] = strdup (optarg);
        continue;

      case 'h':
      case '?':
      default:
        usage ();
    }
  };

  /*
   * no devices specified, use default
   */
  if (no_devices == 0)
  {
    devices[0] = "/dev/kbd0";
    devices[1] = "/dev/kbd1";
    no_devices = 2;
    if (debug)
    {
      fprintf (stderr, "using default devices\n");
    };
  };

  /*
   * switch syscons to first keyboard
   */
#ifndef TEST
  for (i = -1; ++ i < no_devices;)
#else
  for (i = 1; i == 1; ++ i)
#endif
  {
    if (set_keyboard (devices[i]) == 0)
    {
      if (debug)
      {
        fprintf (stderr, "selecting keyboard %s\n", devices[i]);
      };
      break;
    }
  };

  for (;;)
  {
    /*
     * try to open all devices for select
     */
    FD_ZERO (& ifds);
    maxfd = -1;
#ifndef TEST
    for (i = -1; ++ i < no_devices;)
#else
    for (i = 0; i == 0; ++ i)
#endif
    {
      fd = open (devices[i], O_RDONLY|O_NONBLOCK);
      if (fd >= 0)
      {
        if (debug)
        {
          fprintf (stderr, "polling %s", devices[i]);
        };
        if (debug >= 2)
        {
          fprintf (stderr, " fd=%u", fd);
        };
        if (debug)
        {
          fprintf (stderr, "\n");
        };
        FD_SET (fd, & ifds);
        if (fd > maxfd)
        {
          maxfd = fd;
        }
      }
    };

    if (maxfd < 0)
    {
      fprintf (stderr, "could not open any device\n");
      exit (1);
    };
    if (detach && ! detached && ! debug)
    {
      daemon (0, 0);
      detached = 1;
    };

    ofds = ifds;
    if (debug >= 2)
    {
      fprintf (stderr, "polling maxfd=%u\n", maxfd);
    };

    /*
     ***** !!! never returns !!!
     */
    if (select (maxfd, & ofds, NULL, NULL, NULL) == -1)
    {
      /*
       * got signal
       */
      exit (0);
    };

    if (debug)
    {
      fprintf (stderr, "polled successfully\n");
    };

    /*
     * find first keyboard where select returned some activity
     */
    for (fd = -1; ++ fd < maxfd; ++ fd)
    {
      if (FD_ISSET (fd, & ofds))
      {
        if (debug)
        {
          fprintf (stderr, "switching keyboard\n");
        };
        if (debug >= 2)
        {
          fprintf (stderr, " fd=%u", fd);
        };
        if (debug)
        {
          fprintf (stderr, "\n");
        };
        /*
         * switch to keyboard with activity
         */
        set_keyboard_fd (fd);
        FD_ZERO (& ofds);
      }
      else if (FD_ISSET (fd, & ifds))
      {
        if (debug >= 2)
        {
          fprintf (stderr, "closing fd=%u\n", fd);
        };
        close (fd);
      }
    }
  }
}



More information about the freebsd-hackers mailing list