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

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);
class_destroy(cl);

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)
  {
    class_destroy(cl);
    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);
    class_destroy(cl);
    unregister_chrdev_region(first, 1);
    return -1;
  }
  return 0;
}

static void __exit ofcd_exit(void) /* Destructor */
{
  cdev_del(&c_dev);
  device_destroy(cl, first);
  class_destroy(cl);
  unregister_chrdev_region(first, 1);
  printk(KERN_INFO "Alvida: ofcd unregistered");
}

module_init(ofcd_init);
module_exit(ofcd_exit);
MODULE_LICENSE("GPL");
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.

  • Ashish

    Good explanations.. thanks

    • http://twitter.com/anil_pugalia Anil Pugalia

      You are welcome.

  • http://www.facebook.com/fayazvf Fayaz Ahmed

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

    • http://twitter.com/anil_pugalia Anil Pugalia

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

  • http://twitter.com/anil_pugalia Anil Pugalia

    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

  • Pranit Kothari

    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.

    • http://www.facebook.com/anil.pugalia Anil Pugalia

      Thanks for your candid feedback.

  • Vineel Kumar Reddy Kovvuri
  • sandeep

    excellent work

    • http://www.facebook.com/anil.pugalia Anil Pugalia

      Thanks for the appreciation.

  • Livin Crux

    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.

    • anil_pugalia

      Which kernel version are you using?

      • http://twitter.com/anil_pugalia Anil Pugalia

        Please use MKDEV instead of MKNOD

    • srikanth

      I think it is mkdev()

      MKDEV(int major, int minor) -> to make it dev_t type

      • http://twitter.com/anil_pugalia Anil Pugalia

        It is actually a macro and is MKDEV() only. The above error is for wrongly using MKNOD instead of MKDEV

  • isenthilkumar

    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).”

  • Mike DiGesu

    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!

    • anil_pugalia

      With which kernel version are you getting this error?

      • http://twitter.com/anil_pugalia Anil Pugalia

        These APIs and their prototypes have been changing over the kernel versions.

  • akash

    hi … o some places i hv seen people using register_chrdev() function , wht is this … ??

    • akash

      also they use this register_chrdev( ) function without doing alloc_chardev_region() .. is it correct … pls reply

      • http://twitter.com/anil_pugalia Anil Pugalia

        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.

        • akash

          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

          • http://twitter.com/anil_pugalia Anil Pugalia

            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.

  • Amit Raj

    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..

    • http://twitter.com/anil_pugalia Anil Pugalia

      Right now, not planned, as I completed the series in Nov ’12.

  • Krishna Kishore

    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?

    • anil_pugalia

      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.

  • Anup

    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.

    • anil_pugalia

      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”.

  • virendra kumar thakur

    thank u very very much to share information , so many my question’s answer is clear now. explained in very nice ,

    • http://twitter.com/anil_pugalia Anil Pugalia

      Thanks for your appreciation.

  • jagadish

    Its very clear and to the point
    Good job Guys
    keep it up!

    • http://twitter.com/anil_pugalia Anil Pugalia

      Thanks dude :)

  • http://www.facebook.com/iamsvKris Vamsi Krishna

    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?

    • http://twitter.com/anil_pugalia Anil Pugalia

      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.

  • santosh

    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?

    • http://twitter.com/anil_pugalia Anil Pugalia

      My bad. It is MKDEV only.

  • pteaches

    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 ?

    • http://twitter.com/anil_pugalia Anil Pugalia

      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.

      • pteaches

        thank you sir for replying!

    • Najmuddin

      Hi

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

      http://www.tldp.org/LDP/lkmpg/2.4/html/c577.htm

      http://stackoverflow.com/questions/3016107/what-is-tagged-structure-initialization-syntax

      Thanks,
      Najmuddin

  • Audhil

    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??

    • anil_pugalia

      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
      follows:
      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”)))
      {
      class_destroy(cl);
      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);
      class_destroy(cl);
      unregister_chrdev_region(first, 1);
      return ret;
      }
      return 0;
      }

  • deep

    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-

    Driver:write()

    Driver:write()

    Driver:write()

    Driver:read()

    Driver:close()

    no registration/unregistration indication. Plz help me out

    • anil_pugalia

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

      • deep

        thanks i got it.
        But still unable to read and write the device through echo and cat.

        • anil_pugalia

          Read the last para of the article.

          • deep

            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

          • anil_pugalia

            What did you change? Earlier you were able to read & write, right? Your log showed that.

          • deep

            when i run $dmesg output is-

            device registered

            device unregistered

            device registered

            Driver:write()

            Driver:write()

            Driver:write()
            ..

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

            ..

            Driver:read()

            Driver:close()

            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

          • anil_pugalia

            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.

  • lucky

    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.

    • anil_pugalia

      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

  • Rohan

    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.

    • anil_pugalia

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

  • Sreeju

    Hello Sir,

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

    So
    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”
    files.

    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?

    • anil_pugalia

      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.

      • Sreeju

        Thank you sir.

  • Sreeju

    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??

    • anil_pugalia

      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.

      • Sreeju

        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” );
        class_destroy(cl);
        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;

        }

        • anil_pugalia

          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.

          • Sreeju

            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]#

          • anil_pugalia

            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.

          • Sreeju

            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?

          • anil_pugalia

            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.

          • Sreeju

            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??

          • anil_pugalia

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

          • Sreeju

            Is there any mistake in my booting parameters attached in my previous post ?

          • anil_pugalia

            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.

  • sachin

    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() ?

    • Guest

      Ohh got it, I was doing file operation(cdev_init()) before creating cdev structure.

    • sachin

      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.

      • anil_pugalia

        Most likely you are not unregistering your major number in your destructor, which gets called on rmmod.

        • sachin

          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?`

          • anil_pugalia

            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.

  • llc

    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()

    • anil_pugalia

      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

  • Sreeju

    Sir,

    How can I change the baud rate,parity,no of bits etc.. of my serial device using ioctl ?

    Regards

    Sreeju

    • anil_pugalia

      Use the library function tcsetattr

  • Sreeju

    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?

    Regards
    Sreeju

    • anil_pugalia

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

  • Najmuddin

    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,

    Najmuddin

    • anil_pugalia

      Check out the condition they are encapsulated in. It is an error handling case, i.e. in case the operation fails.

  • Najmuddin

    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,
    Najmuddin

    • anil_pugalia

      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.

  • Sreeju

    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?

    • anil_pugalia

      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.

  • Sreeju

    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.

    • anil_pugalia

      You may start with the Linux Device Drivers book by Rubini book – IInd edition

  • Conor

    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.

    • Conor

      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 lwn.net, 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 ;-)

      • Conor

        Sorry, me again. Can I point that (much to my surprise) the class_create and device_create calls (http://lxr.free-electrons.com/source/drivers/base/class.c#L249) 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).

        • anil_pugalia

          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.

      • anil_pugalia

        Thanks for posting your research.

  • neorf

    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 ;-)

    • anil_pugalia

      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

      • neorf

        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
        mynull
        but /dev/mynull has not been created…
        maybe have i to include udev bin inside my ramdisk? i use an ELDK distribution…

        • anil_pugalia

          Yes, next step is to get udev or its light-weight version mdev into your ramdisk.

  • anupam

    Hi sir would u please suggest me which is the best book for linux drivers for beginners? thanks

    • anil_pugalia

      Essential Linux Device Drivers by Sreekrishnan Venkateswaran

  • Satish Kumar

    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.
    Regards
    gsatish.net

    • anil_pugalia

      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.

      • satish

        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.

        • anil_pugalia

          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: http://www.opensourceforu.com/2010/12/writing-your-first-linux-driver/ by appropriately changing the KERNEL_SOURCE variable, and adding the ARCH & CROSS_COMPILE options to the actions of the default & clean targets.

        • anil_pugalia

          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: http://www.opensourceforu.com/2010/12/writing-your-first-linux-driver/ by appropriately changing the KERNEL_SOURCE variable, and adding the ARCH & CROSS_COMPILE options to the actions of the default & clean targets.

          • satish

            Thank you very much, I understood the mistake I am doing.I will try with these changes and hope it works well.
            Thank you for support.

          • anil_pugalia

            You’re welcome. Once you try out the same, please share your learning here.

  • Sunitha Parunandi

    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
    Password:
    su: Authentication failure
    sunitha@SUNITHA:~/modul$ sudo echo “Hello World” > /dev/mynull
    bash: /dev/mynull: Permission denied

    • anil_pugalia

      Try sudo bash, and then try the echo there – it should work.

      • pravin

        How to enable default read write permission from the user space for the generated char file during module initialization itself.

        • anil_pugalia

          Either using chmod or by adding udev rules.

  • Alexandre Léonard

    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 ?

    • anil_pugalia

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

  • Aditya

    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

    • anil_pugalia

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

All published articles are released under Creative Commons Attribution-NonCommercial 3.0 Unported License, unless otherwise noted.
Open Source For You is powered by WordPress, which gladly sits on top of a CentOS-based LEMP stack.

Creative Commons License.