Pages

Writing an Example Driver From Scratch: Chapter- 2 The init function

This is how our driver code looks as of now. We have the header files and the dummy device array.



The next step is to start with the init function.

The new kernel versions use a cdev structure to keep track of all the character devices, hence we would need a cdev structure for our device too.

struct cdev *kernel_cdev;

Let us call our cdev as kernel_cdev as declared above.
We will use dynamic allocation of major number for our device as that is the safest way to assign a major number to your device.
The function that will help us allocate a major number dynamically is

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);

The arguments being passed to the function are
dev -> The dev_t variable type,which will get the major number that the kernel allocates.
firstminor -> The first minor number in case you are looking for a series of minor numbers for your driver.
count -> The number of contiguous set of major numbers that you want to be allocated.
name -> Name of your device that should be associated with the major numbers. The same name will appear in /proc/devices.

The dev_t variable to be passed to the above function can be declared as follows.

dev_t devno

We will allocate minor numbers starting from 0, which is a common practice.
We need only one device number allocated as it is only a single driver for one device so count=1.
Name = char_arr_dev
you can select any name of your comfort. so our call to alloc_chrdev_region would look like this



Where ret is an "int" variable. If alloc_chrdev_region returns a value less than 0, then the alloc_chrdev_region failed to allocate a major number, and your init function should exit with a error message.
Hence we add a check to confirm the allocation



If the alloc_chrdev_region succeeds then the dev_no variable will have the combination major number that the kernel has allocated to your driver and the first minor number that we had selected.
To extract the major number from the dev_no we can use the macro MAJOR. MAJOR takes as input a dev_t variable and returns the major number in intiger format.



So that after doing an "insmod" of your driver you run the command dmesg and see the major number allocated which is printed by the printk.
If you were allocating the major number statically, then we would need to convert the integer number to dev_t format by combining it with the corresponding minor number which is done by the macro MKDEV.

MKDEV takes two integer numbers as input, Major number and minor number, and converts them into one dev_t type number.

dev_no = MKDEV(Major,0)

Where "dev_no" is a variable of type dev_t. This "dev" can be used for registering the driver.

Once we have the "dev_no" number we can create create our cdev structure.

Allocation:



This will initialize the stand alone "kernel_cdev" structure at runtime.
Next step is allocating the fops to the cdev structure. (We will cover fops in the next chapter).



These two assignments sets up the structure as required for us. Now we can inform the kernel about this cdev structure using the cdev_add call.

int cdev_add(struct cdev *dev, dev_t num, unsigned int count);

Where
dev: is the cdev structure that we have setup num: is the dev_t number having the major and minor number in it, the first number the device responds to . count: The number of number of device numbers that are associated with this cdev structure.

In our case the call would as follows



Again, if the "ret" value is less than 0 then it means the kernel could not add the cdev structure and hence we need to exit the init module.



If the cdev_add happens with out any errors we are almost done with the init function. Only thing left is initializing the device semaphore.



Cleanup Function:

Cleanup is pretty simple.We just need to delete the cdev structure using the call cdev_del
void cdev_del(struct cdev *dev);

Which in our case would be



Then unregister the device using

void unregister_chrdev_region(dev_t first, unsigned int count);
Where
first: first dev_t number of a contagious set of drivers that we want to unregister.
count: The number of drivers to be unregistered.

In our case it would be



Now we need to inform the kernel which are our init and cleanup functions
So we make use of the functions module_init and module_exit



Using the knowledge gained above try writing the complete init and cleanup functions, we will look at the code in the beginning of the next chapter.

Next Chapter fops Implementation

9 comments:

  1. Hi,
    Heads up!
    Your 'Chapter-1 Header files' link is broken.

    ReplyDelete
  2. Great tut. This is the way to explain to newbies!!!!
    AJ

    ReplyDelete
  3. awsm start up!! muchhhh easy way to begin with char drivers!!!!
    ...

    ReplyDelete
  4. unregister_chrdev_region(Major, 1) is wrong imho
    function prototype is:
    void unregister_chrdev_region(dev_t first, unsigned int count) so you should use dev_no as first param, not the major number.
    unregister_chrdev_region(dev_no, 1)

    There is another function, which can be used to unregister with Major:
    int unregister_chrdev(unsigned int major, const char *name)

    ReplyDelete
  5. Hi thanks for pointing that out have corrected it.

    ReplyDelete
  6. i wondered why this cdev..it's very useful for a beginner like me !! thanks!!

    ReplyDelete