Creating a basic character device driver for Linux

This tutorial shows how to create a Linux kernel module that will register a simple character device. Character devices support operations like reading/writing data and sending IOCTL codes. A typical example of a character device would be a COM port. In this tutorial we will create a virtual device that produces a stream of messages like this:

This is file #X, iteration Y. Z file handle(s) are now active...

Each time a new user-mode application opens a handle to the device the file number will be increased. Each file instance will have its own iteration counter. The device will produce a one-second delay between the iterations.

Before you begin please follow the basic KGDB tutorial or one of the other basic tutorials to ensure that you can create and debug a basic kernel module.

  1. Start Visual Studio. Select File->New Project->VisualKernel->Linux Kernel Module Wizard:Image
  2. Select “character device” as the project template on the first page of the wizard:Image
  3. On the next page select the Linux machine you are targeting:Image
  4. On the third wizard page specify the directory where you want to store the source files:Image
  5. On the last page select the method of connecting to the kernel debugger (e.g. KGDB):Image
  6. Press “Finish” to generate a Visual Studio project. Build it by pressing Ctrl-Shift-B:Image
  7. Start debugging by pressing F5. VisualKernel will insert the module into the kernel and load the symbols for it. Go to the SSH Session window and click the console button:Image
  8. This will open a multi-tabbed SSH window. Run “sudo cat /dev/<Your project name>” command:Image
  9. Open a new tab and run the same command in it. Note how the file number will now be 2 and the device will also report that 2 handles are open:Image
  10. Now we will see how the message stream is generated. Go back to Visual Studio and set a breakpoint in the read function:ImageThe call stack shows how the function was invoked as a result of handling the ‘read’ syscall. Step through the function by pressing F10 to see how the message is generated.
  11. Our device can produce a one-second delay while handling the read() syscall, so to avoid completely hang the user-mode application that is waiting for us, we are using the msleep_interruptible() function. Set a breakpoint on the line returning -EINTR, resume execution, go to the SSH console and press Ctrl-C:ImageThe breakpoint will now hit showing how pressing Ctrl-C resulted in a cancellation of the msleep_interruptible() call.
  12. Now we will explore how the handle counter is implemented. Put a breakpoint in the open function and re-run the last command in the SSH console:ImageYou can see how the open syscall invoked our function that allocated a state object and associated it with the file object.
  13. Finally we will explore how the device registration works. Set a breakpoint in the AllocateBasicCharacterDevice()function. Then go to the GDB Session window and reload the module:Image
  14. When the breakpoint is hit, step through the function to see the steps required to register the device:Image