Device Drivers, Part 5: Character Device Files — Creation & Operations

Device drivers lessons time

Device drivers lessons time

This article is a continuation of the series on Linux device drivers, and carries on the discussion on character drivers and their implementation.

In my previous article, I had mentioned that even with the registration for the <major, minor> device range, the device files were not created under /dev — instead, Shweta had to create them manually, using mknod. However, on further study, Shweta figured out a way to automatically create the device files, using the udev daemon. She also learnt the second step to connect the device file with the device driver — linking the device file operations to the device driver functions. Here is what she learnt.

Automatic creation of device files

Earlier, in kernel 2.4, the automatic creation of device files was done by the kernel itself, by calling the appropriate APIs of devfs. However, as the kernel evolved, kernel developers realised that device files were more related to user-space and hence, as a policy, that is where they ought to be dealt with, not at the kernel. Based on this idea, the kernel now only populates the appropriate device class and device information into the /sys window, for the device under consideration. User-space then needs to interpret it and take appropriate action. In most Linux desktop systems, the udev daemon picks up that information, and accordingly creates the device files.

udev can be further configured via its configuration files to tune the device file names, their permissions, their types, etc. So, as far as the driver is concerned, the appropriate /sys entries need to be populated using the Linux device model APIs declared in <linux/device.h>. The rest should be handled by udev. The device class is created as follows:

struct class *cl = class_create(THIS_MODULE, "<device class name>");

Then, the device info (<major, minor>) under this class is populated by:

device_create(cl, NULL, first, NULL, "<device name format>", ...);

Here, the first is dev_t with the corresponding <major, minor>. The corresponding complementary or the inverse calls, which should be called in chronologically reverse order, are as follows:

device_destroy(cl, first);

Refer to Figure 1 for the /sys entries created using chardrv as the <device class name> and mynull as the <device name format>. That also shows the device file, created by udev, based on the <major>:<minor> entry in the dev file.

Automatic device file creation
Figure 1: Automatic device file creation

In case of multiple minors, the device_create() and device_destroy() APIs may be put in the for loop, and the <device name format> string could be useful. For example, the device_create() call in a for loop indexed by i could be as follows:

device_create(cl, NULL, MKNOD(MAJOR(first), MINOR(first) + i), NULL, "mynull%d", i);

File operations

Whatever system calls (or, more commonly, file operations) we talk of on a regular file, are applicable to device files as well. That’s what we say: a file is a file, and in Linux, almost everything is a file from the user-space perspective. The difference lies in the kernel space, where the virtual file system (VFS) decodes the file type and transfers the file operations to the appropriate channel, like a filesystem module in case of a regular file or directory, and the corresponding device driver in case of a device file. Our discussion focuses on the second case.

Now, for VFS to pass the device file operations onto the driver, it should have been informed about it. And yes, that is what is called registering the file operations by the driver with the VFS. This involves two steps. (The parenthesised code refers to the “null driver” code below.)

First, let’s fill in a file operations structure (struct file_operations pugs_fops) with the desired file operations (my_open, my_close, my_read, my_write, …) and initialise the character device structure (struct cdev c_dev) with that, using cdev_init().

Then, hand this structure to the VFS using the call cdev_add(). Both cdev_init() and cdev_add() are declared in <linux/cdev.h>. Obviously, the actual file operations (my_open, my_close, my_read, my_write) also had to be coded.

So, to start with, let’s keep them as simple as possible — let’s say, as easy as the “null driver”.

The null driver

Following these steps, Shweta put the pieces together, attempting her first character device driver. Let’s see what the outcome was. Here’s the complete code — ofcd.c:

#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>

static dev_t first; // Global variable for the first device number 
static struct cdev c_dev; // Global variable for the character device structure
static struct class *cl; // Global variable for the device class
static int my_open(struct inode *i, struct file *f)
  printk(KERN_INFO "Driver: open()\n");
  return 0;
  static int my_close(struct inode *i, struct file *f)
  printk(KERN_INFO "Driver: close()\n");
  return 0;
  static ssize_t my_read(struct file *f, char __user *buf, size_t
  len, loff_t *off)
  printk(KERN_INFO "Driver: read()\n");
  return 0;
  static ssize_t my_write(struct file *f, const char __user *buf,
  size_t len, loff_t *off)
  printk(KERN_INFO "Driver: write()\n");
  return len;
  static struct file_operations pugs_fops =
  .owner = THIS_MODULE,
  .open = my_open,
  .release = my_close,
  .read = my_read,
  .write = my_write

static int __init ofcd_init(void) /* Constructor */
  printk(KERN_INFO "Namaskar: ofcd registered");
  if (alloc_chrdev_region(&first, 0, 1, "Shweta") < 0)
    return -1;
    if ((cl = class_create(THIS_MODULE, "chardrv")) == NULL)
    unregister_chrdev_region(first, 1);
    return -1;
    if (device_create(cl, NULL, first, NULL, "mynull") == NULL)
    unregister_chrdev_region(first, 1);
    return -1;
    cdev_init(&c_dev, &pugs_fops);
    if (cdev_add(&c_dev, first, 1) == -1)
    device_destroy(cl, first);
    unregister_chrdev_region(first, 1);
    return -1;
  return 0;

static void __exit ofcd_exit(void) /* Destructor */
  device_destroy(cl, first);
  unregister_chrdev_region(first, 1);
  printk(KERN_INFO "Alvida: ofcd unregistered");

MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com>");
MODULE_DESCRIPTION("Our First Character Driver");

Shweta repeated the usual build process, with some new test steps, as follows:

  1. Build the driver (.ko file) by running make.
  2. Load the driver using insmod.
  3. List the loaded modules using lsmod.
  4. List the major number allocated, using cat /proc/devices.
  5. “null driver”-specific experiments (refer to Figure 2 for details).
  6. Unload the driver using rmmod.
'null driver' experiments
Figure 2: 'null driver' experiments

Summing up

Shweta was certainly happy; all on her own, she’d got a character driver written, which works the same as the standard /dev/null device file. To understand what this means, check the <major, minor> tuple for /dev/null, and similarly, also try out the echo and cat commands with it.

However, one thing began to bother Shweta. She had got her own calls (my_open, my_close, my_read, my_write) in her driver, but wondered why they worked so unusually, unlike any regular file system calls. What was unusual? Whatever was written, she got nothing when reading — unusual, at least from the regular file operations’ perspective. How would she crack this problem? Watch out for the next article.


  1. Hi,
    I tried the code snippet above, but no device called mynull was created under /dev. Could you please help me with this?

    • Check out the other steps, if whether entry of /sys/class/chardrv/… was created, if udevd is running using ps ax | grep udevd.

  2. Which distro are you on? And did you check for the creation of the various /sys/class entries, as explained? Also, check if udev is running by the following command:
    ps ax | grep udev

  3. Good work Anil. I am beginner to Linux world and directly jumped to Linux Device Driver programming. After purchasing huge size books on this topic, your articles gives sigh of relief.

  4. adding to above discussion – working of udev deamon

  5. Hi,
    Nice work. In this chapter. When I tried the device_create() for creating a sample device, it gave me the following error about MKNOD

    implicit declaration of function ‘MKNOD’ [-Werror=implicit-function-declaration]

    Anyone knows how to get around this? I included the linux/device.h and linux/kdev_t.h files FYI.

  6. I do not understand why we need to call class_create and device_create() ,so i google it and find the answers and am posting the same.

    “The module creates the device (not the file) with register_chrdev, which returns the value of the major number that was assigned. We then use class_create to create a udev class to contain the device, finally you call device_create to actually make the device file.

    To remove the device, you call device_destroy to remove the device from the class, then class_unregister to remove the class from sysfs. class_destroy is called to delete the class and finally unregister_chrdev to remove the device from the kernel (the device not the file).”

  7. I love this series of articles and they are really helping me understand the topic, but when I tried to run the example above, I got this message in dmesg: “device_create does not work yet for NULL parents” and nothing gets created in my /dev directory. Anyone else getting this and, if so, know how to get around it? Thanks!

      • register_chrdev() is an older variant where you want to register for all minors of a major. You should not do both register… & alloc… but just one of them. both does the same thing – just that register_chrdev_region is for fixed major registration & alloc_chrdev_region is for dynamic major registration.

        • sorry for disturbig you again n agian … actually i am asking about major = register_chrdev(0, “chardev” , &fops); this statement ….

          I hav a book kernel programing guide in that only

          register_chrdev(0, “chardev” , &fops) function is used .. means this function is performing all the tasks of these three functions …alloc_chardev_region() + cdev_init(&c_dev, &pugs_fops); +cdev_add(&c_dev, first, 1) ,,,

          m i right or wrong pls reply ,,,thnx

          • With the first you can not register for only a subset of minor numbers, but instead you’ll get all minor numbers registered for you.

  8. great piece of job…easy to understand…
    Anil can u pls…provide such informative driver development of i2c based devices…like EEPROM,camera,Audio…etc..

  9. Thank You Very much For such a clear explanation.

    Suppose if I want to get the same message what has been written to driver using echo “Hi” > /dev/mynull and store that message in a file on a file system, then what should I do?

    • That’s a non-trivial task – and for that, you would have to go to my 18th to 24th articles – the file system semester project.

      And yes, thanks for the appreciation.

  10. after i perform

    echo “Hello” > /dev/mynull

    I am getting the following error.-> /dev/mynull: Permission denied

    However if i check the file permissions it is crw——-
    Even with root permission i am getting the same error.

    • I guess when you say with root permission, you mean you used sudo. That wouldn’t work. Get into a root shell (say using sudo bash) & then try the same.

      Or, alternatively you may change the owner of the file to yourself (using chown), or change the permissions to all by doing “chmod 666 /dev/mynull”.

  11. hi anil,

    your article is clear.

    But i have a doubt. How is /dev/mynull similar to /dev/null ?

    I checked the (major, minor) tuple for both the devices but they are different.

    But when i tested the behavior of the device with cat and echo, it seems they are similar

    How do we ensure that a custom built device is similar to another existing device? Can we do only by comparing their behaviors or do they share any concrete similarity so that we can say that a device is similar to another device?

    • Typically device files with different major numbers don’t have identical behaviour. Here, we just created it for fun and understanding. And also, practically there is no need to figure out such similarities, and hence no automated way has been deviced to do such a task. The typical way, as you said, would be to test or even better check out the driver code corresponding to each of the device files.

  12. I am yet to try it out but have one doubt…
    you have mentioned following uses to API
    device_create(cl, NULL, first, NULL, “”, …);

    /* here first is dev_t type*/

    but after few lines to it…
    device_create(cl, NULL, MKNOD(MAJOR(first), MINOR(first) + i), NULL, “mynull%d”, i);

    I have seen MKDEV is the macro to get the dev_t in part 4, but the later usecase uses MKNOD in the second argument. Are these two same?

  13. Hi, I am having some basic queries regarding the null driver code i.e., ofcd.c.
    who is calling the functions (my_open, my_close, my_read, my_write) and also providing parameters for these functions?

    one more thing I never used .(dot) operator inside the structure. How can we use this .(dot) operator inside the structure

    static struct file_operations pugs_fops =


    .owner = THIS_MODULE,

    .open = my_open,

    .release = my_close,

    .read = my_read,

    .write = my_write

    }; ? what is the meaning of using .(dot) operator here ?

    • It is the VFS which calls all these functions providing the corresponding parameters. Read the first para under the section “File Operations” to get more clarity.

      . (dot) here is not the dot operator but it is an gcc extension to allow initialization of a particular field of the structure. And, as far as I know, this has been adopted in the latest ANSI standards, as well.

    • Hi

      Even i was not knowing about this… Got more info while googling in the below links:- Might be useful to others..


  14. Dear Anil, I am series follower of your blogs. I done the above with my ARM board, but at “$ ls /sys/class/chardev” I can see a directory instead of link. But the same works fine with my laptop. But the problem persists with my ARM board and also there is no such “/dev/mynull” is created in /dev directory. Any clues??

    • Are you saying that /sys/class/chardrv/mynull/dev is a directory? If
      this is coming up fine, then the reason for /dev/mynull not getting
      created could be udevd not running in the system. Moreover, there are
      some corrections in the above code, as follows. Correct the init code as
      static int __init ofcd_init(void) /* Constructor */
      int ret;
      struct device *dev_ret;

      printk(KERN_INFO “Namaskar: ofcd registered”);
      if ((ret = alloc_chrdev_region(&first, 0, 1, “Shweta”)) < 0)
      return ret;
      if (IS_ERR(cl = class_create(THIS_MODULE, “chardrv”)))
      unregister_chrdev_region(first, 1);
      return PTR_ERR(cl);
      if (IS_ERR(dev_ret = device_create(cl, NULL, first, NULL, “mynull”)))
      unregister_chrdev_region(first, 1);
      return PTR_ERR(dev_ret);

      cdev_init(&c_dev, &pugs_fops);
      if ((ret = cdev_add(&c_dev, first, 1)) < 0)
      device_destroy(cl, first);
      unregister_chrdev_region(first, 1);
      return ret;
      return 0;

  15. Hello Sir

    I have loaded driver using insmod. But when i run-

    $echo “Hello” > /dev/mynull or $cat /dev/mynull

    nothing happed just cursor blinks.

    when i run $dmesg | tail 5 ,output is-






    no registration/unregistration indication. Plz help me out

    • You are tailing just the last 5 lines. Registration has happened before that. Just type dmesg without a tail and check out your log.

          • I tried it again . when i give in root mode $echo “Hello” > /dev/mynull it gives following ERROR
            bash:echo:write error :Invalid argument
            in user mode it give following EROR
            bash: /dev/mynull :permission denied

          • when i run $dmesg output is-

            device registered

            device unregistered

            device registered




            .. (long series of Driver:write())




            but when i give in root mode $echo “Hello” > /dev/mynull it gives following ERROR
            bash:echo:write error :Invalid argument
            in user mode it give following EROR
            bash: /dev/mynull :permission denied

          • Did you change the write code? Do you have your driver loaded? Does your echo generate some output in the dmesg? Debug on these lines and figure out what’s going wrong. You must be missing out something basic.

  16. sir can you please explain the usage of static struct class *cl; &
    cl = class_create(THIS_MODULE, “chardrv”)
    i have google it but not able to find any relevant information on that.

    • Once you do that it would create a directory under /sys/class with the name chardrv, which can be further populated with next device_create for udev to pick up and create the corresponding device file automatically

  17. hello sir
    My question is little bit apart from this topic.

    I want to develop BSP (Board support Package ) on Linux platform for a customize board with freescale processor. Could you guide me from where I should start i mean from device driver development or from u boot or linux kernel. what should be the steps.

    • Start from the Linux kernel and the board related changes in the kernel. Check out the board specific configs and their related code.

  18. Hello Sir,

    I’m trying to create a built in
    driver for my ARM 926 board running on kernel 2.6.34.

    copied the ofcd.c file and modified the necessary Kconfig and
    Makefile so that the ofcd.o file is generated along with related
    “modules.builtin”,”modules.order”, “built-in.o”

    On compiling and checking the /proc/devices, I
    could find “Shweta” but in /dev no device files were there
    …So I created one using the “mknod” command.

    now when giving the followind command
    echo Hello > /dev/testDrvr only the

    Driver: open()
    Driver: read()
    Driver: close()

    are displayed… what could be the reason for that?

    • Thanks for attaching the screenshot. That immediately showed the problem. The reads are coming from your cat on your device file. And the writes due to echo are not coming, as you missed out the redirection sign > before the device file.

  19. Sir,

    For built in drivers is it possible to create a device file in /dev directory without creating it manually using the “mknod” command ??…. i.e, I need my device’s file to be created in /dev directory when the kernal loads.. Is there any buli in function used for this??

    • For built-in drivers, it should work the same way as for the dynamically loaded drivers. Check out by dynamically loading the driver first. Possibly, there also the device file is not getting created. And in that case, you need to make sure that the daemon udev or its embedded version mdev is running and configured properly.

      • thank you fro replying..

        I tried two things

        1. Tried to insert my driver dynamically

        2. Got the latest kernel source for my board from the vendor website with their patch.

        As per your assumption, in both the above cases, the /dev directory doesn’t populated the device driver file. Only the /sys/class/chardev/ directory is getting created which contains the “dev, power, subsystem, uevent”… Please check the attached image..

        Here is the initialisation code:

        static int init_gpio(void)
        //init_result = register_chrdev( 0, “gpio”, &FileOps );

        init_result = alloc_chrdev_region( &first, 0, 1, “Shweta” );

        if( 0 > init_result )
        printk( KERN_ALERT “Device Registration failedn” );
        return -1;
        printk(KERN_ALERT “n: n”,MAJOR(first),MINOR(first));

        if ( (cl = class_create( THIS_MODULE, “chardev” ) ) == NULL )
        printk( KERN_ALERT “Class creation failedn” );
        unregister_chrdev_region( first, 1 );
        return -1;

        if( device_create( cl, NULL, first, NULL, “keypad” ) == NULL )
        printk( KERN_ALERT “Device creation failedn” );
        unregister_chrdev_region( first, 1 );
        return -1;

        cdev_init( &c_dev, &FileOps );

        if( cdev_add( &c_dev, first, 1 ) == -1)
        printk( KERN_ALERT “nDevice addition failedn” );
        device_destroy( cl, first );
        class_destroy( cl );
        unregister_chrdev_region( first, 1 );
        return -1;

        return 0;


        • First of all, I’d like you to correct the code, as per my reply below to Audhil. There are some mistakes in the above article, especially in error handling of the class_create & device_create. Once fixed, let’s see what happens, when you dynamically load your driver. Post your observations.

          • Sir,
            I modified the init code as per your reply to Audhil… then grep for UDEV and MDEV which resulted as follows…

            [root@nxp dev]#
            [root@nxp dev]# ps ax | grep udev
            448 root 1440 S grep udev
            [root@nxp dev]# ps ax | grep mdev
            450 root 1440 S grep mdev
            [root@nxp dev]#

          • Yup, you do not have any of them running. Just type the “udevd -d” command on shell and check out if that makes any difference. If it says command not found, you need to get that for your system.

          • Abolutely right,

            [root@nxp /]# udevd -d
            -/bin/sh: udevd: command not found
            [root@nxp /]#

            How can I get this daemon? Is this one board/processsor specific? And how can I check if this service is running in my system?

          • If you are using busybox, it might be already part of it – just that it is not selected to compile along with. So, you may have to recompile busybox with the right selections.

          • Sir,

            I downloaded the latest version of Busybox and have done some building and compilation steps (I selected the “mdev” option). Here I’m attaching my UBOOT env variables and the screen shot of loading kernel … It gets struck at a place saying

            “Freeing init memory: 1244K
            Failed to execute /init
            Kernel panic – not syncing: No init found. Try passing init= option to kernel. ”

            What could be the problem??

          • /init typically points to the busybox, which seems like is not properly available for the boot up sequence.

          • From the boot parameters, it looks like that by default you would be using the mtdblock3 as your root partition. Check in your system if that really is the case.

  20. I wrote similar code to access char device. I had to create a device manually using mknod. When I included your code to auto-create the device, it crashes my system.
    The last error I see is it goes to cdev_init(). Is there any way I can mail you the code ? Can you please tell us more about cdev_init() ?

    • Every time I insert my module, it major number is reduced by one. Is this normal? or am I doing something wrong. Please give me some pointers.

        • Ohh, thanx, I was not unregistering major number if cdev_add() was less than ZERO. But now I have a question, `Why was it reducing? and how far it can go?`

          • Because it gets next free available in a higher to lower fashion. And it would be possible, as long as, there would be free major number available to be allocated.

  21. Nice blog – thanks! Any idea why a write (echo) wouldn’t work?

    [733] Fri Aug 09@21:21 drivers
    $ uname -a
    Linux utopia 3.10.5-1-ARCH #1 SMP PREEMPT Mon Aug 5 08:04:22 CEST 2013 x86_64 GNU/Linux

    [734] Fri Aug 09@21:21 drivers
    $ sudo echo “hello” > /dev/mynull
    -bash: /dev/mynull: Permission denied

    [735] Fri Aug 09@21:21 drivers
    $ sudo cat /dev/mynull

    [736] Fri Aug 09@21:21 drivers
    $ dmesg | tail -10
    [34369.170536] Namaskar: ofcd registered
    [34394.037704] Driver: open()
    [34394.037738] Driver: read()
    [34394.037751] Driver: close()

    • As sudo runs echo as root but the redirections > is still under normal user. Do a sudo bash and them try the echo “hello” > /dev/null

  22. Sir,
    I’m working on LPC350 board. I have tested the character driver built in to the kernel. It’s having a baud rate of 115200bps and stop bits = 1. Now I would like to change/ add an option to change the configuration settings of this serial device ? How can I do it using ioctl? Is there any API for that?


    • Check out the man page of tty_ioctl. Alternatively, try using tcsetattr, do an strace, and you’d know, the ioctls it is calling.

  23. Hi Sir,

    Why are we using these functions device_destroy(cl, first); class_destroy(cl);

    in the init module ?
    What is the possibility that these functions shall be executed in the init module ?

    Thanks in Advance,


  24. Hi Sir,

    I wrote a char driver and inserted the module. I already used rmmod,

    I still see my devices left in /proc/devices, like this:

    248 CHR
    249 CHR
    250 CHR

    Now I cannot insert any module, everytime I use insmod the shell tells me:

    Error: could not insert module test.ko: Device or resource busy

    I’m asking how to remove these registered devices from /proc/devices.

    Thanks in Advance,

    • Check out your exit function – I am sure that either the unregister_chrdev_region has not been called or has been called with wrong parameters in the exit function.

  25. Sir, SPI comes under the character drivers, right? Can I use the same code for character driver in this page for creating an SPI driver? If not, why? And how should I begin?

    • SPI is a horizontal (protocol) layer and so can theoretically come with any of the three verticals (char, block, nw). But as typical SPI devices do byte oriented transactions, the typical vertical is char, and hence character drivers. Though many a times for simple SPI devices, developers often expose them as files over /sys – so even that is acceptable.

  26. Sir,

    I want to try writing a network driver on 2.6.39. I gathered some details on it. How can I start ? Need your valuable guidance.

  27. Nice driver introduction. Something up to date for once! I have a small question about cdev though. LDD3 states to add
    c_dev.owner = THIS_MODULE;
    c_dev.ops = &pugs_fops;
    Checking the source for cdev_add etc and checking for equivalence I find that the second line is unnecessary but the first line I’m not sure about. If I check if (cdev.owner == THIS_MODULE) I find it isn’t. Should I follow LDD3 in this regard? Thanks.

    • I think I can answer this myself. You need to set ops if you use cdev_alloc() and not cdev_init() like you do.

      From, originally setting the owner field of the cdev structure to THIS_MODULE protected against ill-advised module unloads. That now appears in the file_operations struct so I guess it isn’t needed in cdev. I see code diffs in the kernel where it was there and now isn’t (eg: drivers/char/raw.c). I think an LDD4 is in order!
      In other words Shweta’s code is fine ;-)

      • Sorry, me again. Can I point that (much to my surprise) the class_create and device_create calls ( do not return NULL on error but rather a ERR_PTR. This is -errno cast into a pointer and should be checked with IS_ERR and converted to a number with PTR_ERR. Thanks for the great series – I’ve learnt a lot by also cross referencing to the Linux tree (using lxr above).

        • Your point is perfectly valid. As soon as I realised that, I posted the correction comment – please go down these comments to see the same. It is a reply to Audhil’s comment.

  28. Hello, really nice blog…
    but just 1 question…
    i’ve tried your example for null driver, but i get:
    # ls -l /dev/mynull
    ls: /dev/mynull: No such file or directory

    I’m developing a driver for an embedded board, but i get some problem registering the device… i’m working on 2.6.33 and for example, i don’t have the /sys dir… Have i to enable sometime in kernel menuconfig?

    Thank you so much ;-)

    • Possibly the sysfs is not mounted &/or udev may not be running. mount the sysfs as follows: mount -t sysfs none /sys; and check the udev as follows: ps ax| grep udev, and then may be start it as follows: ./udev

      • ok thanks… i’ve tried and… some updates… i’ve created /etc/fstab with:
        none sys sysfs defaults,noatime 0 0
        then mkdir /sys and mount -a… now i’ve /sys dir, i use insmod null.ko and inside:
        /sys/class/chardrv # ls
        but /dev/mynull has not been created…
        maybe have i to include udev bin inside my ramdisk? i use an ELDK distribution…

  29. Hi , I am going through the drivers , it is working fine with x86 systems. If I want to try the same module in ARM board/ Beaglebone with different compiler, it is showing lot of errors like EFAULT, etc.. To compile the same module for ARM arch. what are extra headers files I have to include . Thank you in advance.

    • Are you trying to directly cross compile the driver? You need to compile it through your boards kernel source/header – basically you have to change the KERNEL_SOURCE line in the Makefile to point to the path of your board’s kernel.

      • Thank you for reply. I am trying the above driver to compile it on Qemu for versatile board.. I am using following command for executing the above driver : “make ARCH.=arm CROSS_COMPILE=/arm-none-linux-gnueabi- ” I am getting the error w.r.t the Header files inclusion. Can you please suggest me How the above driver can be compiled for any ARM Board.
        Thank you in advance.

        • That’s what I am trying to mention. That’s not the right way. First you need to have the kernel headers for your versatile board, and then you need to build as mentioned in this article: by appropriately changing the KERNEL_SOURCE variable, and adding the ARCH & CROSS_COMPILE options to the actions of the default & clean targets.

        • That’s what I am trying to mention. That’s not the right way. First you need to have the kernel headers for your versatile board, and then you need to build as mentioned in this article: by appropriately changing the KERNEL_SOURCE variable, and adding the ARCH & CROSS_COMPILE options to the actions of the default & clean targets.

  30. Hi Sir,

    i have loaded the module , but while checking the mynull , permission denied ,i tried with sudo but still the same ..?.

    sunitha@SUNITHA:~/modul$ ls -l /dev/mynull
    crw——- 1 root root 250, 0 Dec 21 00:42 /dev/mynull
    sunitha@SUNITHA:~/modul$ su
    su: Authentication failure
    sunitha@SUNITHA:~/modul$ sudo echo “Hello World” > /dev/mynull
    bash: /dev/mynull: Permission denied

  31. Hi Anil, thanks for your tutorial, it’s really helpful. I was wondering about the MKNOD in the device_create for multiple devices. Shouldn’t it be MKDEV instead ? To create a dev_t object ?

    • Thanks for your appreciation. And yes, you are correct. That’s a mistake, as mentioned in the reply below to the comment by santosh.

  32. When i try to write to my device i get following error
    bash: /dev/MyDev: Permission denied

    I do have write permission for this device

    ~/systemprog/3.kernel_module/char_driver$ ls -l /dev/MyDev
    crw——- 1 root root 250, 0 Jul 6 22:32 /dev/MyDev

    • You have write permissions for root, not others. So, either write into it as root, or add write permissions to others as well.

      • Hi Sir,
        I am Jyothi. Presently I am working on MPC8323E-rdb Board, I am trying to add serial device and map it to physical address by using setserial code. I am able to create the device but i am not able to map to physical location . Can you suggest any approach i have to follow for mapping? And here uart chip is on local bus not on PCI.

        • What do you mean by mapping using setserial? Devices are typically already mapped through their corresponding drivers.

          • Hello Sir,
            I want to create device under serial driver(ttyS) at run time…Without using module ..Is it possible in linux.? And I want to give UART hardware address to that device…If it is possible can you suggest me that how to do it ..I am using ltib for cross compilation..Can you please help in this.


          • There is no short cut without going through module. Module has to be there and it also should have configured the appropriate address.

      • Hello Anil,

        Thanks for nice Driver Tutorial. I have a doubt that..

        I followed the following commands to create a device file to
        access my UART driver and I gave address 0xE0004600 (a part of my
        memory and can be accessible on my hardware) for the uart, but I am
        getting following error. I am using vendor’s UART xr1678x Module code.

        $ mknod /dev/ttyXR78x0 c 40 0
        $ chmod a+w /dev/ttyXR78x0
        $ cat /dev/ttyXR78x0
        cat: Read Error: Input/output error

        please help me in understanding this type of issues.

        Thanks in Advance.

        Siva Prakash Reddy Narreddy

          • As per my Hardware, I can access the uart with that address and I mentioned this address in module code. The offset 0x4600 points to uart’s Tx/Rx registers. The major number 40 given by me and already it is points Serial Driver. I also tried it with different major number 250 which is free.

          • the complete code I took from the following website..


            and I modified XR78x_BASE_IO as 0xfe004600 to access my uart

          • Can you explain some more clearly, because as a newbe I am not understanding the tracing.?

          • I just meant – walk through the read function, put printk’s and figure out where exactly is it failing – like a usual debugging technique.

  33. Device drivers part 6 is not opening. Can admin check it once.

    • Hi Aswin, I am able to open it. Anyways, if you are still not able to open it, you may check its latest updated version from my blog at SysPlay:

  34. i try with two minor no. and created two device file. but its not showing in /dev and /sys/class///dev

    if(maj_min < 0)
    return -1;
    cl = class_create(THIS_MODULE , "shubh_class");
    if(cl == NULL)
    printk(KERN_INFO "error in creating classn");
    return -1;
    major = MAJOR(maj_min);
    printk(KERN_INFO "major number = %d n",MAJOR(maj_min));
    if(device_create(cl,NULL,MKDEV(major,minor),NULL,"shubh_device") == NULL)
    printk(KERN_INFO "error in creating devicen");
    return -1;
    printk(KERN_INFO "device created. minor=%dn",minor);

    • Firstly, the code in the above article has errors. For example, the return value of class_create() & device_create() needs to be checked using the macro IS_ERR, not directly by comparing with NULL. Check out the latest updated article & code here:

      Moreover, the problem could be because you are creating the same device file name for both. Change the device_create line as follows:

      if (IS_ERR(device_create(cl, NULL, MKDEV(major, minor), NULL, “shubh_device%d”, minor))

  35. Dear all,
    I had tried to insert that module into my system by using
    “insmod”. But when I read kernel message with “dmesg” command, I figured
    out that this module was not inserted in right way. I got a warning
    “ofcd: module verification failed: signature and/or required key missing – tainting kernel”
    Does any one know how to fix this error? Please help me

  36. Hi,

    Interesting article. With kernel version 3.18.3+ when checking the “/dev/mynull” with ls -command (beginning of Figure 2) only root has read and write permissions to the “mynull” file:

    pi@raspberrypi / $ ls -l /dev/mynull
    crw——- 1 root root 248, 0 Mar 29 14:29 /dev/mynull

    And reading and writing fails even when trying these with sudo.
    When I grant read and write permissions for all users, everything starts to work like in Figure 2. Is this as specified?

  37. My mynull driver can’t stop writing. Terminal hangs when I do “$ echo “Hello Universe” > /dev/mynull”.
    When I close the terminal and open again, I get this for dmesg | tail :
    (! 465)-> dmesg | tail
    [ 9768.698243] DRIVER: write()
    [ 9768.698243] DRIVER: write()
    [ 9768.698244] DRIVER: write()
    [ 9768.698245] DRIVER: write()
    [ 9768.698245] DRIVER: write()
    [ 9768.698246] DRIVER: write()
    [ 9768.698247] DRIVER: write()
    [ 9768.698277] DRIVER: write()
    [ 9768.713518] systemd-journald[321]: /dev/kmsg buffer overrun, some messages lost.
    [ 9768.727077] DRIVER: close()

    (! 466)->

  38. I used your code and tried “cat /dev/mynull”, it is printing all the other prints as shown in the screenshot but not “Driver: write()”. I dont see any “Driver: write()” print in the log file (dmesg).

  39. int my_open (struct inode *i, struct file *f) {
    printk(KERN_INFO “Anji: Driver: open()\n”);
    return 0;

    ssize_t my_read(struct file *f, char *buf, size_t len, loff_t *off) {
    printk(KERN_INFO “Anji: Driver: read()\n”);
    return 0;

    ssize_t my_write(struct file *f, const char *buf, size_t len, loff_t *off) {
    printk(KERN_INFO “Anji: Driver: write()\n”);
    return 0;

    int my_close(struct inode *i, struct file *f) {
    printk(KERN_INFO “Anji: Driver: close()\n”);
    return 0;
    struct file_operations f_ops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .read = my_read,
    .write = my_write,
    .release = my_close
    static int __init mod_init(void)
    printk(KERN_INFO “Init fn\n”);

    /* get dynamic MIN, MAJOR numbers */
    if (alloc_chrdev_region(&first, 0, 3, “Anji”) /dev/mynull, the console hanged
    [root@manager CharDri]# cat /dev/mynull
    [513042.243687] Anji: Driver: open()
    [513042.243691] Anji: Driver: read()
    [513042.243693] Anji: Driver: close()


Please enter your comment!
Please enter your name here