Benzle Input Devices Driver



Laboratory objectives¶

  1. Benzle Input Devices Driver
  2. Input Mapper
  3. Benzle Input Devices Driver Updater
  4. Benzle Input Devices Driver Device
  5. Benzle Input Devices Driver Touchpad
  6. Output Devices Of Computer
  • understand the concepts behind character device driver
  • understand the various operations that can be performed on character devices
  • working with waiting queues

Overview¶

In UNIX, hardware devices are accessed by the user through special devicefiles. These files are grouped into the /dev directory, and system callsopen, read, write, close, lseek, mmap etc. areredirected by the operating system to the device driver associated with thephysical device. The device driver is a kernel component (usually a module)that interacts with a hardware device.

In the UNIX world there are two categories of device files and thusdevice drivers: character and block. This division is done by the speed,volume and way of organizing the data to be transferred from the device to thesystem and vice versa. In the first category, there are slow devices, whichmanage a small amount of data, and access to data does not require frequentseek queries. Examples are devices such as keyboard, mouse, serial ports,sound card, joystick. In general, operations with these devices (read, write)are performed sequentially byte by byte. The second category includes deviceswhere data volume is large, data is organized on blocks, and search is common.Examples of devices that fall into this category are hard drives, cdroms, ramdisks, magnetic tape drives. For these devices, reading and writing is done atthe data block level.

For the two types of device drivers, the Linux kernel offers different APIs.If for character devices system calls go directly to device drivers, in case ofblock devices, the drivers do not work directly with system calls. Inthe case of block devices, communication between the user-space and the blockdevice driver is mediated by the file management subsystem and the block devicesubsystem. The role of these subsystems is to prepare the device driver’snecessary resources (buffers), to keep the recently read data in the cachebuffer, and to order the read and write operations for performance reasons.

Drivers; Audio and Multimedia; SoundMAX HD Audio Driver 6. Analog Devices SoundMAX Driver for Windows XP, 2000, 2003 and Vista. This driver is compatible with 32 and 64bit operating systems. Recommended Resolution. Reinstall the device driver manually. From Start, search for device manager and select Device Manager from the results. Right-click the device in the list. Select Uninstall from the menu that appears. After the device is uninstalled, choose Action on the menu bar. Select Scan for hardware changes to reinstall the driver. Note You may be prompted to provide the path.

Majors and minors¶

In UNIX, the devices traditionally had a unique, fixed identifier associatedwith them. This tradition is preserved in Linux, although identifiers can bedynamically allocated (for compatibility reasons, most drivers still use staticidentifiers). The identifier consists of two parts: major and minor. The firstpart identifies the device type (IDE disk, SCSI disk, serial port, etc.)and the second one identifies the device (first disk, second serial port,etc.). Most times, the major identifies the driver, while the minor identifieseach physical device served by the driver. In general, a driver will have amajor associate and will be responsible for all minors associated with thatmajor.

As can be seen from the example above, device-type information can be foundusing the ls command. The special character files are identified by the ccharacter in the first column of the command output, and the block type by thecharacter b. In columns 5 and 6 of the result you can see themajor, respectively the minor for each device.

Certain major identifiers are statically assigned to devices (in theDocumentation/admin-guide/devices.txt file from the kernel sources). When choosing theidentifier for a new device, you can use two methods: static (choose a numberthat does not seem to be used already) or dynamically. In /proc/devices are theloaded devices, along with the major identifier.

To create a device type file, use the mknod command; the command receives thetype (block or character), major and minor of the device(mknodnametypemajorminor). Thus, if you want to create a character devicenamed mycdev with the major 42 and minor 0, use the command:

To create the block device with the name mybdev with the major 240 and minor 0the command will be:

Next, we’ll refer to character devices as drivers.

Data structures for a character device¶

In the kernel, a character-type device is represented bystructcdev, a structure used to register it in thesystem. Most driver operations use three important structures:structfile_operations, structfile and structinode.

structfile_operations

As mentioned above, the character device drivers receive unaltered system callsmade by users over device-type files. Consequently, implementation of a characterdevice driver means implementing the system calls specific to files: open,close, read, write, lseek, mmap, etc. These operations aredescribed in the fields of the structfile_operations structure:

It can be noticed that the signature of the function differs from the systemcall that the user uses. The operating system sits between the user andthe device driver to simplify implementation in the device driver.

open does not receive the parameter path or the various parameters that controlthe file opening mode. Similarly, read, write, release, ioctl, lseekdo not receive as a parameter a file descriptor. Instead, these routines receive asparameters two structures: file and inode. Both structures represent a file,but from different perspectives.

Most parameters for the presented operations have a direct meaning:
  • file and inode identifies the device type file;
  • size is the number of bytes to be read or written;
  • offset is the displacement to be read or written (to be updatedaccordingly);
  • user_buffer user buffer from which it reads / writes;
  • whence is the way to seek (the position where the search operation starts);
  • cmd and arg are the parameters sent by the users to the ioctl call (IOcontrol).

inode and file structures¶

An inode represents a file from the point of view of the file system. Attributesof an inode are the size, rights, times associated with the file. An inode uniquelyidentifies a file in a file system.

The file structure is still a file, but closer to the user’s point of view.From the attributes of the file structure we list: the inode, the file name,the file opening attributes, the file position. All open files at a given timehave associated a file structure.

To understand the differences between inode and file, we will use an analogyfrom object-oriented programming: if we consider a class inode, then the filesare objects, that is, instances of the inode class. Inode represents the staticimage of the file (the inode has no state), while the file represents thedynamic image of the file (the file has state).

Returning to device drivers, the two entities have almost always standard waysof using: the inode is used to determine the major and minor of the device onwhich the operation is performed, and the file is used to determine the flagswith which the file was opened, but also to save and access (later) privatedata.

The file structure contains, among many fields:

  • f_mode, which specifies read (FMODE_READ) or write(FMODE_WRITE);
  • f_flags, which specifies the file opening flags (O_RDONLY,O_NONBLOCK, O_SYNC, O_APPEND, O_TRUNC, etc.);
  • f_op, which specifies the operations associated with the file (pointer tothe file_operations structure );
  • private_data, a pointer that can be used by the programmer to storedevice-specific data; The pointer will be initialized to a memory locationassigned by the programmer.
  • f_pos, the offset within the file

The inode structure contains, among many information, an i_cdevfield, which is a pointer to the structure that defines the characterdevice (when the inode corresponds to a character device).

Implementation of operations¶

To implement a device driver, it is recommended that you create a structurethat contains information about the device, information used in the module. Inthe case of a driver for a character device, the structure will contain a cdevstructure field to refer to the device. The following example uses the structmy_device_data:

Benzle

A structure like my_device_data will contain the data associated with a device.The cdev field (cdev type) is a character-type device and is used to record itin the system and identify the device. The pointer to the cdev member can befound using the i_cdev field of the inode structure (using the container_ofmacro). In the private_data field of the file structure, information can bestored at open which is then available in the read, write, release, etc.routines.

Registration and unregistration of character devices¶

The registration/unregistration of a device is made by specifying the major andminor. The dev_t type is used to keep the identifiers of a device (both majorand minor) and can be obtained using the MKDEV macro.

For the static assignment and unallocation of device identifiers, theregister_chrdev_region and unregister_chrdev_region functions are used:

It is recommended that device identifiers be dynamically assigned to thealloc_chrdev_region function.

Below sequence reserves my_minor_count devices, starting with my_majormajor and my_first_minor minor (if the max value for minor is exceeded,move to the next major):

After assigning the identifiers, the character device will have to beinitialized (cdev_init) and the kernel will have to be notified(cdev_add). Thecdev_add function must be called only after the device is ready to receivecalls. Removing a device is done using the cdev_del function.

The following sequence registers and initializes MY_MAX_MINORS devices:

While the following sequence deletes and unregisters them:

Note

initialization of the struct my_fops used the initializationof members by name, defined in C99 standard (see designatedinitializers and the file_operations structure). Structuremembers who do not explicitly appear in this initializationwill be set to the default value for their type. Forexample, after the initialization above, my_fops.mmap willbe NULL.

Access to the address space of the process¶

A driver for a device is the interface between an application and hardware. Asa result, we often have to access user-space data. Accessing it can not be donedirectly (by de-referencing a user-space pointer). Direct access of auser-space pointer can lead to incorrect behavior (depending on architecture, auser-space pointer may not be valid or mapped to kernel-space), a kernel oops(the user-mode pointer can refer to a non-resident memory area) or securityissues. Proper access to user-space data is done by calling the macros /functions below:

All macros / functions return 0 in case of success and another value in case oferror and have the following roles:

  • put_user store the value val to user-space address address;Type can be one on 8, 16, 32, 64 bit (the maximum supported type depends on thehardware platform);
  • get_user analogue to the previous function, only that val will be set to avalue identical to the value at the user-space address given by address;
  • copy_to_user copies n bytes from the kernel-space, from the addressreferenced by from in user-space to the address referenced by to;
  • copy_from_user copies n bytes from user-space from the addressreferenced by from in kernel-space to the address referenced by to.

A common section of code that works with these functions is:

Open and release¶

The open function performs the initialization of a device. In most cases,these operations refer to initializing the device and filling in specific data(if it is the first open call). The release function is about releasingdevice-specific resources: unlocking specific data and closing the device ifthe last call is close.

In most cases, the open function will have the following structure:

A problem that occurs when implementing the open function is access control.Sometimes a device needs to be opened once at a time; More specifically, do notallow the second open before the release. To implement this restriction, youchoose a way to handle an open call for an already open device: it can returnan error (-EBUSY), block open calls until a release operation, or shut downthe device before do the open.

At the user-space call of the open and close functions on the device, callmy_open and my_release in the driver. An example of a user-space call:

Read and write¶

The read and write operations are reaching the device driver as aresult of a userspace program calling the read or write system calls:

The read and write functions transfer data between the device and theuser-space: the read function reads the data from the device and transfers itto the user-space, while writing reads the user-space data and writes it to thedevice. The buffer received as a parameter is a user-space pointer, which iswhy it is necessary to use the copy_to_user or copy_from_user functions.

The value returned by read or write can be:

  • the number of bytes transferred; if the returned value is less than the sizeparameter (the number of bytes requested), then it means that a partialtransfer was made. Most of the time, the user-space app calls the system call(read or write) function until the required data number is transferred.
  • 0 to mark the end of the file in the case of read ; if write returns thevalue 0 then it means that no byte has been written and that no error hasoccurred; In this case, the user-space application retries the write call.
  • a negative value indicating an error code.

To perform a data transfer consisting of several partial transfers, thefollowing operations should be performed:

  • transfer the maximum number of possible bytes between the buffer receivedas a parameter and the device (writing to the device/reading from the devicewill be done from the offset received as a parameter);
  • update the offset received as a parameter to the position from which thenext read / write data will begin;
  • return the number of bytes transferred.

The sequence below shows an example for the read function that takesinto account the internal buffer size, user buffer size and the offset:

The images below illustrate the read operation and how data istransferred between the userspace and the driver:

  1. when the driver has enough data available (starting with the OFFSETposition) to accurately transfer the required size (SIZE) to the user.
  2. when a smaller amount is transferred than required.

We can look at the read operation implemented by the driver as a response to auserpace read request. In this case, the driver is responsible for advancingthe offset according to how much it reads and returning the read size (whichmay be less than what is required).

The structure of the write function is similar:

The write operation will respond to a write request from userspace. Inthis case, depending on the maximum driver capacity (MAXSIZ), it canwrite more or less than the required size.

ioctl¶

In addition to read and write operations, a driver needs the ability to performcertain physical device control tasks. These operations are accomplished byimplementing a ioctl function. Initially, the ioctl system call used Big KernelLock. That’s why the call was gradually replaced with its unlocked versioncalled unlocked_ioctl. You can read more on LWN:http://lwn.net/Articles/119652/

cmd is the command sent from user-space. If a value is being sent from theuser-space call, it can be accessed directly. If a buffer is fetched, the argvalue will be a pointer to it, and must be accessed through the copy_to_useror copy_from_user.

Before implementing the ioctl function, the numbers corresponding to thecommands must be chosen. One method is to choose consecutive numbers startingat 0, but it is recommended to use _IOC(dir,type,nr,size) macrodefinitionto generate ioctl codes. The macrodefinition parameters are as follows:

  • dir represents the data transfer (_IOC_NONE , _IOC_READ,_IOC_WRITE).
  • type represents the magic number (Documentation/ioctl/ioctl-number.txt);
  • nr is the ioctl code for the device;
  • size is the size of the transferred data.

The following example shows an implementation for a ioctl function:

At the user-space call for the ioctl function, the my_ioctl function of thedriver will be called. An example of such a user-space call:

Waiting queues¶

It is often necessary for a thread to wait for an operation to finish,but it is desirable that this wait is not busy-waiting. Using waitingqueues we can block a thread until an event occurs. When the conditionis satisfied, elsewhere in the kernel, in another process, in aninterrupt or deferrable work, we will wake-up the process.

A waiting queue is a list of processes that are waiting for a specificevent. A queue is defined with the wait_queue_head_t type and canbe used by the functions/macros:

The roles of the macros / functions above are:

  • init_waitqueue_head() initializes the queue; to initialize thequeue at compile time, you can use the DECLARE_WAIT_QUEUE_HEAD macro;
  • wait_event() and wait_event_interruptible() adds the current thread to thequeue while the condition is false, sets it to TASK_UNINTERRUPTIBLE orTASK_INTERRUPTIBLE and calls the scheduler to schedule a new thread; Waitingwill be interrupted when another thread will call the wake_up function;
  • wait_event_timeout() and wait_event_interruptible_timeout() have the sameeffect as the above functions, only waiting can be interrupted at the end ofthe timeout received as a parameter;
  • wake_up() puts all threads off from state TASK_INTERRUPTIBLE andTASK_UNINTERRUPTIBLE in TASK_RUNNING status; Remove these threads from thequeue;
  • wake_up_interruptible() same action, but only threads with TASK_INTERRUPTIBLEstatus are woken up.

A simple example is that of a thread waiting to change the value of a flag. Theinitializations are done by the sequence:

A thread will wait for the flag to be changed to a value other than zero:

While another thread will change the flag value and wake up the waiting threads:

Exercises¶

Important

To solve exercises, you need to perform these steps:

  • prepare skeletons from templates
  • build modules
  • copy modules to the VM
  • start the VM and test the module in the VM.

The current lab name is device_drivers. See the exercises for the task name.

The skeleton code is generated from full source examples located intools/labs/templates. To solve the tasks, start by generatingthe skeleton code for a complete lab:

You can also generate the skeleton for a single task, using

Once the skeleton drivers are generated, build the source:

Then, copy the modules and start the VM:

The modules are placed in /home/root/skels/device_drivers/<task_name>.

Alternatively, we can copy files via scp, in order to avoid restarting the VM.For additional details about connecting to the VM via the network, please check Connecting to the VM.

Review the Exercises section for more detailed information.

Warning

Before starting the exercises or generating the skeletons, please run git pull inside the Linux repo,to make sure you have the latest version of the exercises.

If you have local changes, the pull command will fail. Check for local changes using gitstatus.If you want to keep them, run gitstash before pull and gitstashpop after.To discard the changes, run gitreset--hardmaster.

If you already generated the skeleton before gitpull you will need to generate it again.

0. Intro¶

Using LXR find the definitionsof the following symbols in the Linux kernel:

  • structfile
  • structfile_operations
  • generic_ro_fops
  • vfs_read()

1. Register/unregister¶

The driver will control a single device with the MY_MAJOR major andMY_MINOR minor (the macros defined in the kernel/so2_cdev.c file).

  1. Create /dev/so2_cdev character device node using mknod.

  2. Implement the registration and deregistration of the device with the nameso2_cdev, respectively in the init and exit module functions. Implement TODO 1.

    Hint

    Read the section Registration and unregistration of character devices

  3. Display, using pr_info, a message after the registration and unregistrationoperations to confirm that they were successful. Then load the module into the kernel:

    And see character devices in /proc/devices:

    Identify the device type registered with major 42 . Note that /proc/devicescontains only the device types (major) but not the actual devices (i.e. minors).

    Note

    Entries in /dev are not created by loading the module. These can be createdin two ways:

    • manually, using the mknod command as we did above.
    • automatically using udev daemon
  4. Unload the kernel module

2. Register an already registered major¶

Modify MY_MAJOR so that it points to an already used major number.

Hint

See /proc/devices to get an already assigned major.

See errno-base.hand figure out what does the error code mean.Return to the initial configuration of the module.

3. Open and close¶

Run cat/dev/so2_cdev to read data from our char device.Reading does not work because the driver does not have the open function implemented.Follow comments marked with TODO 2 and implement them.

  1. Initialize your device
    • add a cdev struct field to so2_device_data structure.
    • Read the section Registration and unregistration of character devices in the lab.
  2. Implement the open and release functions in the driver.
  3. Display a message in the open and release functions.
  4. Read again /dev/so2_cdev file. Follow the messages displayed by the kernel.We still get an error because read function is not yet implemented.

Note

Benzle Input Devices Driver

The prototype of a device driver’s operations is in the file_operationsstructure. Read Open and release section.

4. Access restriction¶

Restrict access to the device with atomic variables, so that a single processcan open the device at a time. The rest will receive the “device busy” error(-EBUSY). Restricting access will be done in the open function displayed bythe driver. Follow comments marked with TODO 3 and implement them.

  1. Add an atomic_t variable to the device structure.
  2. Initialize the variable at module initialization.
  3. Use the variable in the open function to restrict access to the device. Werecommend using atomic_cmpxchg().
  4. Reset the variable in the release function to retrieve access to the device.
  5. To test your deployment, you’ll need to simulate a long-term use of yourdevice. To simulate a sleep, call the scheduler at the end of the device opening:

Note

The advantage of the atomic_cmpxchg function is that it can check theold value of the variable and set it up to a new value, all in oneatomic operation. Read more details about atomic_cmpxchgAn example of use is here.

5. Read operation¶

Implement the read function in the driver. Follow comments marked with TODO4 and implement them.

  1. Keep a buffer in so2_device_data structure initialized with the value of MESSAGE macro.Initializing this buffer will be done in module init function.
  2. At a read call, copy the contents of the kernel space buffer into the userspace buffer.
    • Use the copy_to_user() function to copy information from kernel space touser space.
    • Ignore the size and offset parameters at this time. You can assume thatthe buffer in user space is large enough. You do not need to check thevalidity of the size argument of the read function.
    • The value returned by the read call is the number of bytes transmittedfrom the kernel space buffer to the user space buffer.
  3. After implementation, test using cat/dev/so2_cdev.

Note

The command cat/dev/so2_cdev does not end (use Ctrl+C).Read the read and write sections and Access to the address space of the processIf you want to display the offset value use a construction of the form:pr_info('Offset:%lldn',*offset); The data type loff_t (used by offset ) is a typedef for long long int.

The cat command reads to the end of the file, and the end of the file issignaled by returning the value 0 in the read. Thus, for a correct implementation,you will need to update and use the offset received as a parameter in the readfunction and return the value 0 when the user has reached the end of the buffer.

Modify the driver so that the cat commands ends:

Benzle Input Devices Driver
  1. Use the size parameter.
  2. For every read, update the offset parameter accordingly.
  3. Ensure that the read function returns the number of bytes that were copiedinto the user buffer.

Note

By dereferencing the offset parameter it is possible to read and move the currentposition in the file. Its value needs to be updated every time a read is donesuccessfully.

6. Write operation¶

Add the ability to write a message into kernel buffer to replace the predefined message. Implementthe write function in the driver. Follow comments marked with TODO5

Ignore the offset parameter at this time. You can assume that the driver buffer islarge enough. You do not need to check the validity of the write function sizeargument.

Note

The prototype of a device driver’s operations is in the file_operationsstructure.Test using commands:

Read the read and write sections and Access to the address space of the process

7. ioctl operation¶

For this exercise, we want to add the ioctl MY_IOCTL_PRINT to display themessage from the IOCTL_MESSAGE macro in the driver.Follow the comments marked with TODO6

For this:

  1. Implement the ioctl function in the driver.
  2. We need to use user/so2_cdev_test.c to call theioctl function with the appropriate parameters.
  3. To test, we will use an user-space program (user/so2_cdev_test.c)which will call the ioctl function with the required arguments.

Note

The macro MY_IOCTL_PRINT is defined in the file include/so2_cdev.h,which is shared between the kernel module and the user-space program.

Read the ioctl section in the lab.

Note

The userspace code is compiled automatically at makebuild andcopied at makecopy.

Because we need to compile the program for qemu machine which is 32 bit,if your host is 64 bit then you need to install gcc-multilib package.

Extra Exercises¶

Ioctl with messaging¶

Add two ioctl operations to modify the message associated with thedriver. Use fixed-length buffer ( BUFFER_SIZE ).

  1. Add the ioctl function from the driver the following operations:
    • MY_IOCTL_SET_BUFFER for writing a message to the device;
    • MY_IOCTL_GET_BUFFER to read a message from your device.
  2. For testing, pass the required command line arguments to theuser-space program.

Note

Read the ioctl and Access to the address space of the processsections of the lab.

Ioctl with waiting queues¶

Add two ioctl operations to the device driver for queuing.

  1. Add the ioctl function from the driver the following operations:
    • MY_IOCTL_DOWN to add the process to a queue;
    • MY_IOCTL_UP to remove the process from a queue.
  2. Fill the device structure with a wait_queue_head_t field and a flag.
  3. Do not forget to initialize the wait queue and flag.
  4. Remove exclusive access condition from previous exercise
  5. For testing, pass the required command line arguments to theuser-space program.

When the process is added to the queue, it will remain blocked in execution; Torun the queue command open a new console in the virtual machine with Alt+F2 ;You can return to the previous console with Alt+F1 . If you’re connected viaSSH to the virtual machine, open a new console.

Note

Read the ioctl and Waiting queues sections in the lab.

O_NONBLOCK implementation¶

Note

If a file is open with the O_NONBLOCK flag, then itsoperations will be non-blocking.

In case data is not available when performing a read, the followinghappens:

  • if the file has been open with O_NONBLOCK, the read callwill return -EWOULDBLOCK.
  • otherwise, the current task (process) will be placed in a waitingqueue and will be unblocked as soon as data becomes available(in our case, at write).
  • To allow unblocking the read operation, remove the exclusive accesscondition from previous exercises.
  • You can use the queue defined for the previous exercise.
  • You can ignore the file offset.
  • Modify the initial size of data to 0, to allow testing.
  • For testing, pass the required command line arguments to theuser-space program.
    • when using the n option, the test program will change the open flagsto O_NONBLOCK and then perform a read.
  • What are the flags used to open the file when running cat/dev/so2_dev?
-->

Non-HID keyboards and mice can connect over multiple legacy buses but still use the same class driver. This section contains details on the class drivers themselves. The following sections goes into details on the controllers.

This topic describes the typical physical configuration of keyboard and mouse devices in Microsoft Windows 2000 and later.

The following figures show two common configurations that employ a single keyboard and a single mouse.

The figure on the left shows a keyboard and a mouse connected to a system bus through independent controllers. A typical configuration consists of a PS/2-style keyboard operated through an i8042 controller, and a serial-style mouse operated through a serial port controller.

The following additional information is important for keyboard and mice manufactures:

  • Keyboards are opened in exclusive mode by the operating system stack for security reasons
  • Windows supports the simultaneous connection of more than one keyboard and mouse device.
  • Windows does not support independent access by a client to each device.

Class driver features

This topic describes the features of the following Microsoft Windows 2000 and later system class drivers:

  • Kbdclass, the class driver for devices of GUID_CLASS_KEYBOARD device class

  • Mouclass, the class driver for devices of GUID_CLASS_MOUSE device class

Kbdclass implements the Kbdclass service and its executable image is kbdclass.sys.

Mouclass implements the Mouclass service and its executable image is mouclass.sys.

Kbdclass and Mouclass each feature:

  • Generic and hardware-independent operation of the device class.

  • Plug and Play, power management, and Windows Management Instrumentation (WMI).

  • Operation of legacy devices.

  • Simultaneous operation of more than one device.

  • Connection of a class service callback routine that a function driver uses to transfer data from the input data buffer of the device to the data buffer of the class driver.

Configuration of device objects

The following figure shows the configuration of device objects for a Plug and Play PS/2-style keyboard and mouse device. Each class driver creates an upper-level class filter device object (filter DO) that is attached to a function device object (FDO) through an optional upper-level device filter DO. An upper-level device filter driver creates the upper-level device filter DO. I8042prt creates the function DO and attaches it to a physical device object (PDO) created by the root bus driver.

PS/2 Keyboard

The keyboard driver stack consists of the following.

  • Kbdclass, the upper-level keyboard class filter driver
  • One or more optional upper-level keyboard filter driver
  • I8042prt, the function driver

PS/2 Mouse

The mouse driver stack consists of the following.

  • Mouclass, the upper-level mouse class filter driver
  • One or more optional upper-level mouse filter driver
  • I8042prt, the function driver

Kbdclass and Mouclass can support more than one device in two different modes. In the one-to-one mode, each device has an independent device stack. The class driver creates and attaches an independent class DO to each device stack. Each device stack has its own control state and input buffer. The Microsoft Win32 subsystem accesses input from each device through a unique file object.

Computer

In the grandmaster mode, the class driver operates all the devices in the following way:

  • The class driver creates both a grandmaster class DO that represents all of the devices and a subordinate class DO for each device.

    The class driver attaches a subordinate class DO to each device stack. Below the subordinate class DO, the device stack is same as that created in the one-to-one mode.

  • The grandmaster class DO controls the operation of all the subordinate DOs.

  • The Win32 subsystem accesses all device input through the file object that represents the grandmaster class device.

  • All device input is buffered in the grandmaster's data queue.

  • The grandmaster maintains a single global device state.

Kbdclass and Mouclass operate in the one-to-one mode if their registry entry value ConnectMultiplePorts is set to 0x00 (under the key HKLMServicesCurrentControlSet<class service>Parameters, where class service is Kbdclass or Mouclass). Otherwise Kbdclass and Mouclass operate in grandmaster mode.

Open and close via the class driver

The Microsoft Win32 subsystem opens all keyboard and mouse devices for its exclusive use. For each device class, the Win32 subsystem treats input from all the devices as if the input came from a single input device. An application cannot request to receive input from only one particular device.

The Win32 subsystem dynamically opens Plug and Play input devices after it receives notification from the Plug and Play manager that a GUID_CLASS_KEYBOARD or GUID_CLASS_MOUSE device interface is enabled. The Win32 subsystem closes Plug and Play devices after it receives notification that an opened interface is disabled. The Win32 subsystem also opens legacy devices by name (for example, 'DeviceKeyboardLegacyClass0'). Note that once the Win32 subsystem successfully opens a legacy device, it cannot determine if the device is later physically removed.

After Kbdclass and Mouclass receive a create request they do the following for Plug and Play and legacy operation:

  • Plug and Play Operation

    If the device is in the Plug and Play started state, the class driver sends the IRP_MJ_CREATE request down the driver stack. Otherwise the class driver completes the request without sending the request down the driver stack. The class driver sets the trusted file that has read access to the device. If there is a grandmaster device, the class driver sends a create request to all the ports that are associated with the subordinate class devices.

  • Legacy Operation

    The class driver sends an internal device control request to the port driver to enable the device.

Connect a service callback to a device

The class drivers must connect their class service to a device before the device can be opened. The class drivers connect their class service after they attach a class DO to a device stack. The function driver uses the class service callback to transfer input data from a device to the class data queue for the device. The function driver's ISR dispatch completion routine for a device calls the class service callback. Kbdclass provides the class service callback KeyboardClassServiceCallback, and Mouclass provides the class service callback MouseClassServiceCallback.

A vendor can modify the operation of a class service callback by installing an upper-level filter driver for a device. The sample keyboard filter driver Kbfiltr defines the KbFilter_ServiceCallback callback, and the sample mouse filter driver Moufiltr defines the MouFilter_ServiceCallback callback. The sample filter service callbacks can be configured to modify the input data that is transferred from the port input buffer for a device to the class data queue. For example, the filter service callback can delete, transform, or insert data.

The class and filter service callbacks are connected in the following way:

  • The class driver sends an internal device connect request down the device stack (IOCTL_INTERNAL_KEYBOARD_CONNECT or IOCTL_INTERNAL_MOUSE_CONNECT). The class connect data is specified by a CONNECT_DATA structure that includes a pointer to the class device object, and a pointer to the class service callback.

  • After the filter driver receives the connect request, it saves a copy of the class connect data, and replaces the request's connect data with filter connect data. The filter connect data specifies a pointer to the filter device object and a pointer to the filter driver service callback. The filter driver then sends the filtered connect request to the function driver.

The class and filter service callbacks are called in the following way:

  • The function driver uses the filter connect data to make the initial callback to the filter service callback.

  • After filtering the input data, the filter service callback uses the class connect data that it saved to make a callback to the class service callback.

Query and set a keyboard device

I8042prt supports the following internal device control requests to query information about a keyboard device, and to set parameters on a keyboard device:

For more information about all keyboard device control requests, see Human Interface Devices Reference.

Scan code mapper for keyboards

In Microsoft Windows operating systems, PS/2-compatible scan codes provided by an input device are converted into virtual keys, which are propagated through the system in the form of Windows messages. If a device produces an incorrect scan code for a certain key, the wrong virtual key message will be sent. This can be fixed by writing a filter driver that analyzes the scan codes generated by firmware and modifies the incorrect scan code to one understood by the system. However, this is a tedious process and can sometimes lead to severe problems, if errors exist in the kernel-level filter driver.

Windows 2000 and Windows XP include a new Scan Code Mapper, which provides a method that allows for mapping of scan codes. The scan code mappings for Windows are stored in the following registry key:

Note There is also a Keyboard Layouts key (notice the plural form) under the Control key, but that key should not be modified.

In the Keyboard Layout key, the Scancode Map value must be added. This value is of type REG_BINARY (little Endian format) and has the data format specified in the following table.

Start offset (in bytes)Size (in bytes)Data
04Header: Version Information
44Header: Flags
84Header: Number of Mappings
124Individual Mapping
.........
Last 4 bytes4Null Terminator (0x00000000)

The first and second DWORDS store header information and should be set to all zeroes for the current version of the Scan Code Mapper. The third DWORD entry holds a count of the total number of mappings that follow, including the null terminating mapping. The minimum count would therefore be 1 (no mappings specified). The individual mappings follow the header. Each mapping is one DWORD in length and is divided into two WORD length fields. Each WORD field stores the scan code for a key to be mapped.

Once the map is stored in the registry, the system must be rebooted for the mappings to take effect. Note that if the mapping of a scan code is necessary on a keypress, the step is performed in user mode just before the scan code is converted to a virtual key. Doing this conversion in user mode can present certain limitations, such as mapping not working correctly when running under Terminal Services.

To remove these mappings, remove the Scancode Map registry value and reboot.

Example 1

The following presents an example. To swap the left CTRL key with the CAPS LOCK key, use a registry editor (preferably Regedt32.exe) to modify the Scancode Map key with the following value:

The following table contains these entries broken into DWORD fields and the bytes swapped.

Value: Interpretation

0x00000000: Header: Version. Set to all zeroes.

0x00000000: Header: Flags. Set to all zeroes.

0x00000003: Three entries in the map (including null entry).

0x001D003A: Left CTRL key --> CAPS LOCK (0x1D --> 0x3A).

0x003A001D: CAPS LOCK --> Left CTRL key (0x3A --> 0x1D).

0x00000000: Null terminator.

Example 2

It is also possible to add a key not generally available on a keyboard or to remove a key that is never used. The following example shows the value stored in Scancode Map to remove the right CTRL key and change the functionality of the right ALT key to work as a mute key:

Input Mapper

The following table contains these entries broken into DWORD fields and the bytes swapped.

Value: Interpretation

0x00000000: Header: Version. Set to all zeroes.

0x00000000: Header: Flags. Set to all zeroes.

0x00000003: Three entries in the map (including null entry).

0xE01D0000: Remove the right CTRL key (0xE01D --> 0x00).

0xE038E020: Right ALT key --> Mute key (0xE038 --> 0xE020).

0x00000000: Null terminator.

After the necessary data is generated, it can be inserted into the registry in several ways.

  • A .reg file can be generated that can be easily incorporated into the system registry using a registry editor.
  • An .inf file can also be created with an [AddReg] section that contains the registry information to be added.
  • Regedt32.exe can be used to manually add the information to the registry.

The Scan Code Mapper has several advantages and disadvantages.

The advantages include:

  • The Mapper can be used as an easy fix to correct firmware errors.
  • Frequently used keys can be added to the keyboard by modifying the map in registry. Keys that aren't often used (for example, right CTRL key) can be mapped to null (removed) or exchanged for other keys.
  • Key locations can be altered easily. Users can easily customize the location of frequently used keys for their benefit.

The following disadvantages are recognized:

  • Once the map is stored in the registry, a system reboot is required to activate it.
  • The mappings stored in the registry work at system level and apply to all users. These mappings cannot be set to work differently depending on the current user.
  • The current implementation restricts the functionality of the map such that mappings always apply to all keyboards connected to the system. It is not currently possible to create a map on a per-keyboard basis.

Query a mouse device

I8042prt supports the following internal device control request to query information about a mouse device:

For more information about all mouse device control requests, see Human Interface Devices Reference.

Registry settings associated with mouse class driver

The following is a list of registry keys associated with the mouse class driver.

Benzle Input Devices Driver Updater

[Key: HKLMSYSTEMCurrentControlSetServicesMouclassParameters]

  • MaximumPortsServiced – Not used on Windows XP and later. Only for Windows NT4.
  • PointerDeviceBaseName – Specifies the base name for the device objects created by the mouse class device driver
  • ConnectMultiplePorts – Determines whether there is one or more than one port device object for each class device object. This entry is used primarily by device drivers.
  • MouseDataQueueSize - Specifies the number of mouse events buffered by the mouse driver. It also is used in calculating the size of the mouse driver's internal buffer in the nonpaged memory pool.

Absolute pointing devices

Benzle Input Devices Driver Device

For devices of type GUID_CLASS_MOUSE, a device's function driver:

Benzle Input Devices Driver Touchpad

  • Handles device-specific input.

  • Creates the MOUSE_INPUT_DATA structures required by MouseClassServiceCallback.

  • Transfers MOUSE_INPUT_DATA structures to the Mouclass data queue by calling MouseClassServiceCallback in its ISR dispatch completion routine.

For an absolute pointing device, the device's function driver must set the LastX, LastY, and Flags members of the MOUSE_INPUT_DATA structures in the following way:

Output Devices Of Computer

  • In addition to dividing the device input value by the maximum capability of the device, the driver scales the device input value by 0xFFFF:

  • The driver sets the MOUSE_MOVE_ABSOLUTE flag in Flags.

  • If the input should be mapped by Window Manager to an entire virtual desktop, the driver sets the MOUSE_VIRTUAL_DESKTOP flag in Flags. If the MOUSE_VIRTUAL_DESKTOP flag is not set, Window Manager maps the input to only the primary monitor.

The following specifies, by type of device, how these special requirements for an absolute pointing device are implemented:

  • HID devices:

    Mouhid, the Windows function driver for HID mouse devices, implements these special requirements automatically.

  • PS/2-style devices:

    An upper-level filter driver is required. The filter driver supplies an IsrHook callback and a class service callback. I8042prt calls the IsrHook to handle raw device input, and calls the filter class service callback to filter the input. The filter class service callback, in turn, calls MouseClassServiceCallback. The combination of the IsrHook callback and the class service callback handles device-specific input, creates the required MOUSE_INPUT_DATA structures, scales the device input data, and sets the MOUSE_MOVE_ABSOLUTE flag.

  • Plug and Play COM port devices that are enumerated by Serenum:

    A Plug and Play function driver is required. The function driver creates the required MOUSE_INPUT_DATA structures, scales the device input data, and sets the MOUSE_MOVE_ABSOLUTE flag before it calls MouseClassServiceCallback.

  • Non-Plug and Play COM port devices:

    A device-specific function driver is required. The function driver creates the required MOUSE_INPUT_DATA structures, scales the device input data, and sets the MOUSE_MOVE_ABSOLUTE flag before it calls MouseClassServiceCallback.

  • Device on an unsupported bus:

    A device-specific function driver is required. The function driver creates the required MOUSE_INPUT_DATA structures, scales the device input data, and sets the MOUSE_MOVE_ABSOLUTE flag before it calls MouseClassServiceCallback.