Embedded 版 (精华区)

发信人: Zinux (Linux技工), 信区: Embedded_system
标  题: Writing an Input Module
发信站: 哈工大紫丁香 (2001年10月26日18:22:34 星期五), 站内信件


Linux Magazine (http://www.linux-mag.com) August 2000



Copyright Linux Magazine ?2000



GEARHEADS ONLY
Writing an Input Module
by Alessandro Rubini

Figure One: An overview of the data flow related to the idiom sample
module.
Last month I gave an overview of the USB kernel subsystem, but I
didn't have the space needed to show real code at work. This month
we'll fill the gap by looking at sample drivers implementing input
devices in the USB framework. The code being introduced has been
developed and tested on version 2.3.99-pre6 of the Linux kernel,
developed and tested on version 2.3.99-pre6 of the Linux kernel,
running on an Intel x86 machine.

The sample module introduced here is called idiom (Input Device for
Intercepting Output of Mice), and its source code is available for
download from ftp://ftp.linux.it/pub/People/rubini/idiom.tar.gz. The
sample module registers itself with the USB kernel subsystem as a
mouse driver and with the input management subsystem as a keyboard
driver. idiom translates mouse movement events into keyboard input
events: it reports arrow events to the input system according to how
the physical mouse is moved.

Since the module registers itself as a USB device, you can't test its
workings if you don't have a USB mouse to generate events. To
partially fill the gap, idiom offers an additional entry point,
/dev/idiom, where you can write text strings that will be converted to
USB keyboard events.

Overview of the Idiom Module

The overall design of the module is depicted in Figure One. As
outlined in the previous article, the USB input device driver must
connect to two different infrastructure: the usbcore device driver
connect to two different infrastructure: the usbcore device driver
that handles hardware events on the USB port and the input module that
collects and dispatches input events. In addition, idiom registers an
entry point with the misc device driver.

The misc device driver is a generic driver: it handles major number 10
and offers entry points (namely, misc_ register and misc_inregister)
to create or delete individual special files. For example, both the
/dev/psaux (PS/2 mouse) and the /dev/rtc (real time clock) fall under
the broad misc umbrella.

Figure One shows how the driver attaches to these three working
environments using kernel facilities. Assuming that you have compiled
the USB kernel subsystem as modules, you'll need to invoke the
following commands as root to test the driver:


insmod usbcore
insmod usb-ohci

insmod input
insmod keybdev


insmod idiom
mknod /dev/idiom c 10$(grep idiom /proc/misc|awk'{print$1}')


The first two commands load the USB machinery and a hardware driver
for your host controller (not needed if you have no host controller on
your computer and you plan to use /dev/idiom instead). The second pair
of commands loads the input management machinery and the driver that
consumes keyboard events. The last pair loads idiom itself and creates
the associated misc device. You should make sure to remove /dev/idiom
when you are done with these tests, or modify idiom.c to use /proc or
devfs instead of the misc device.




Linking to the USB Framework

At load time, the idiom driver (which is heavily based on the official
usbmouse module) registers its own usb_driver data structure. The
callbacks defined in the structure will be called whenever a new
device is attached to the USB bus or an existing device is detached.
device is attached to the USB bus or an existing device is detached.
Let's forget for a while that idiom registers with the misc device, so
that module initialization and shutdown turns out to be as simple as
the following:


static struct usb_driver idiom_usb_driver = {
   name: "idiom",
   probe: idiom_probe,
   disconnect: idiom_disconnect,
};

int init_module(void)
{
   usb_register(&idiom_usb_driver);
   return 0;
}

void cleanup_module(void)
{
   usb_deregister(&idiom_usb_driver);
}



Actual activation of the module happens within the idiom_ probe
function, called whenever a new device is plugged into a USB socket.
The core of the probe function deals with detecting whether the new
device is of the right kind or not, which is in this case a USB mouse
device.

If device identification in the probe function goes well, a new device
structure must be allocated. One way to do this is with a static
structure, but then your module will not support more than one device
of the same type at the same time. In order to avoid this, the probe
function allocates a new device structure using the following code:


/* allocate and zero a new data structure
   for the new device */
idiom = kmalloc(sizeof(struct idiom_device),
                GFP_KERNEL);
if (!idiom) return NULL; /* failure */
memset(idiom, 0, sizeof(*idiom));



The new structure must include a "USB Request Block'' structure, which
needs to be initialized and submitted back to the USB subsystem:


/* fill the URB data structure using the
   FILL_INT_URB macro */
{
static void idiom_irq(struct urb *urb);
   /* forward declaration */
int pipe = usb_rcvintpipe(udev,
              endpoint->bEndpointAddress);
int maxp = usb_maxpacket(udev, pipe,
                     usb_pipeout(pipe));
FILL_INT_URB(&idiom->urb,
             udev, pipe,
             idiom->data,
             maxp > 3 ? 3 : maxp,
             idiom_irq, idiom,
             endpoint->bInterval);
}

/*register theURBwithinthe USBsubsystem*/
/*register theURBwithinthe USBsubsystem*/
if (usb_submit_urb(&idiom->urb)) {
    kfree(idiom);
    return NULL; /* failure */
}


The macro FILL_INT_URB above is used to fill the URB data structure to
describe "interrupt transfers'' with the device. The USB specification
defines four types of transfers -- control, interrupt, bulk, and
isochronous. The mouse device falls in the "interrupt'' class.
Interrupt transfers are not actually interrupt-based but rather are
replies to polling performed by the host controller (driver).

The parameters of the macro call are used to initialize the fields of
the URB data structure (refer to linux/usb. h for the details). The
interesting arguments here are idiom_irq and maxp. The former is the
pointer to the complete handler, which eats data packets after the
completion of each transfer. The latter is the maximum length of the
data buffer, which is set to three for the mouse driver.

During device operation, everything is accomplished by the complete
handler, in this case idiom_irq. The function is handed a pointer to
handler, in this case idiom_irq. The function is handed a pointer to
the structurb and looks in the data buffer to retrieve data, after
checking that no error has occurred:


static void idiom_irq(struct urb *urb)
{
   struct idiom_device*idiom = urb->context;
   signed char *data = idiom->data;
   struct input_dev *idev = &idiom->idev;

   if (urb->status!= USB_ST_NOERROR) return;

   /* ignore data[0] which reports mouse
      buttons */
   idiom->x += data[1];
      /* accumulate movements */
   idiom->y += data[2];
}


When the device is detached, the disconnect handler gets called. The
handler must unregister the submitted URB and release memory allocated
handler must unregister the submitted URB and release memory allocated
to the device:


usb_unlink_urb(&idiom->urb);
kfree(idiom);


These few lines of code are all that's needed for a device driver to
interact with the USB framework. Even though things get slightly more
complicated for different types of data transfers, the general rules
outlined here apply and the source code for a real device driver will
be a good reference for the curious reader.




Feeding the System with Input Data

We already know that reacting to USB data transfers is only half of a
USB device driver's job -- the other half deals with communicating
data to the rest of the Linux kernel so that it can be dispatched and
consumed. As far as input devices are concerned, communication with
consumed. As far as input devices are concerned, communication with
the external world is performed by the input module, and the USB
device driver needs only to feed data to that module.

Communication with input is laid out in the usual steps: registration,
communication, cleanup. The first step is performed by the same probe
function that submits a URB to the USB framework; the driver must fill
a structinput_dev to describe the input it might feed and register to
that structure. This is accomplished by the following lines:


/* tell the features of this input device:
   fake only keys */
idiom->idev.evbit[0] = BIT(EV_KEY);

/* and tell which keys: only the arrows */
set_bit(KEY_UP,    idiom->idev.keybit);
set_bit(KEY_LEFT,  idiom->idev.keybit);
set_bit(KEY_RIGHT, idiom->idev.keybit);
set_bit(KEY_DOWN,  idiom->idev.keybit);

/* and register the input device itself */
input_register_device(&idiom->idev);
input_register_device(&idiom->idev);


What's most apparent here is that everything is laid out in bitmasks:
the driver must state that it only reports keyboard events and also
specify which keys will be generated (in this case, the four arrow
keys). If you check linux/input.h you'll find several bit indexes
defined, as other input channels are handled similarly.

When the input device is registered with the input framework, the
function input_event can be called by the driver whenever it has new
data to feed. Within idiom, data is received by idiom_irq, and it's
that very function that routes new data to its final destination.

Since idiom converts high-resolution mouse events to low-resolution
keyboard events, I chose to only generate a keypress for every 10
pixels of mouse motion. Within idiom_irq, therefore, these lines are
repeated four times (one for each possible direction):


while (idiom->x < -10) {
   input_report_key(idev, KEY_LEFT, 1);
      /* keypress */
      /* keypress */
   input_report_key(idev, KEY_LEFT, 0);
      /* release */
   idiom->x += 10;
}


These few lines of code turn a USB mouse into an arrow farm whenever
you load idiom instead of usbmouse.

The function input_report_key is actually a wrapper to input_event:
the first call shown above which expands to input_event(idev, EV_KEY,
KEY_ LEFT, 1). While I personally would call input_event, it's more
common to use the wrapper macro instead.

The last step in input management is unregistering. This is
accomplished by a single line of code in idiom_ disconnect. Just
before the idiom data structure is deallocated from memory:


input_unregister_device(&idiom->idev);



Using /dev/idiom

In order to play with idiom, even in case no USB mouse or USB-capable
computer is there, I added to the module support for a misc device.
Use of the device is simple and straightforward: a new idiom device is
created by the module whenever the /dev/idiom file is opened, and a
mouse movement is simulated whenever an uppercase or lowercase letter
in the set "u, d, l, r'' is written to the device's special file
/dev/idiom. For example, "echo uuull /dev/idiom" will generate three
up-arrows and two left-arrows.

The implementation of the device entry point is confined to the final
part of the source file for idiom, except for registering and
unregistering calls, which are invoked at module initialization and
shutdown. Even though use of a "simulated'' USB device cannot
duplicate driving a real one, this trick should help those with no USB
hardware to begin in this new arena of peripheral devices.

Be warned, however, that this discussion limits itself to the simplest
available device, and driving something like a digital camera is quite
a different kind of task, with new and interesting problems to face.

up-arrows and two left-arrows.
The implementation of the device entry point is confined to the final
part of the source file for idiom, except for registering and
unregistering calls, which are invoked at module initialization and
shutdown. Even though use of a "simulated'' USB device cannot
duplicate driving a real one, this trick should help those with no USB
hardware to begin in this new arena of peripheral devices.

Be warned, however, that this discussion limits itself to the simplest
available device, and driving something like a digital camera is quite
a different kind of task, with new and interesting problems to face.

--

  puke! 
  技工而已

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.239.152]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:205.499毫秒