Monthly Archives: June 2013

Character device files: Creation & Operations

This fifth article, which is part of the series on Linux device drivers, is continuation of the various concepts of character drivers and their implementation, dealt with in the previous article.

<< Fourth Article

In our previous article, we noted that even with the registration for <major, minor> device range, the device files were not created under the /dev, rather Shweta had to create them by hand using mknod. However, on further study, Shweta figured out a way for the automatic creation of the device files using the udev daemon. She also learnt the second step for connecting the device file with the device driver – “Linking the device file operations to the device driver functions”. Here are her learnings.

Automatic creation of device files

Earlier in kernel 2.4, automatic creation of device files was done by the kernel itself, by calling the appropriate APIs of devfs. However, as kernel evolved, kernel developers realized that device files are more of a user space thing and hence as a policy only the users should deal with it, not the kernel. With this idea, now kernel only populates the appropriate device class & device info into the /sys window for the device under consideration. And then, the user space need to interpret it and take an 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 using its configuration files to tune the device file names, their permissions, their types, etc. So, as far as driver is concerned, the appropriate /sys entries need to be populated using the Linux device model APIs declared in <linux/device.h> and the rest would be handled by udev. Device class is created as follows:

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

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

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

where first is the 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 9, 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.

Figure 9: Automatic device file creation

Figure 9: Automatic device file creation

In case of multiple minors, device_create() and device_destroy() APIs may be put in 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, MKDEV(MAJOR(first), MINOR(first) + i), NULL, "mynull%d", i);

File operations

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

Now, for VFS to pass the device file operations onto the driver, it should have been told about that. And yes, that is what is called registering the file operations by the driver with the VFS. This involves two steps. (The parenthesised text below refers to the ‘null driver’ code following it.) First, is to 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 to initialize the character device structure (struct cdev c_dev) with that, using cdev_init(). The second step is to 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 by Shweta. So, to start with, Shweta kept them as simple as possible, so as to say, as easy as the “null driver”.

The null driver

Following these steps, Shweta put all the pieces together to attempt her first character device driver. Let’s see what was the outcome. Here’s the complete code:

#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 */
{
	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;
}

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@sarika-pugs.com>");
MODULE_DESCRIPTION("Our First Character Driver");

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

  • Build the driver (.ko file) by running make.
  • Load the driver using insmod.
  • List the loaded modules using lsmod.
  • List the major number allocated using cat /proc/devices.
  • “null driver” specific experiments (Refer to Figure 10 for details).
  • Unload the driver using rmmod.
Figure 10: “null driver” experiments

Figure 10: “null driver” experiments

Summing up

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

But one thing started bothering Shweta. She had got her own calls (my_open, my_close, my_read, my_write) in her driver, but how are they working so unusually unlike any regular file system calls. What’s so unusual? Whatever I write, I get nothing when read – isn’t that unusual, at least from regular file operations’ perspective. Any guesses on how would she crack this nut? Watch out for the next article.

Sixth Article >>

Notes:

  1. For using a fixed major number, you may use register_chrdev_region() instead of alloc_chrdev_region().
  2. Use kernel version >= 2.6.3x for the class_create() and the device_create() APIs to compile properly work as explained. As, before that version they have been rapidly evolving and changing.
  3. Kernel APIs (like class_create(), device_create()) which returns pointers, should be checked using IS_ERR macro instead of comparing with NULL, as NULL is zero (i.e. success and not an error). These APIs return negative pointers on error – error code from which could be extracted using PTR_ERR. See the usage in the above example.

Other References:

  1. Working of udev daemon
   Send article as PDF   

Mathematics made easy with minimal Octave

This fifth article of the mathematical journey through open source, introduces the minimal, octave can do for you.

<< Fourth Article

You wanted a non-programmers way of doing mathematics and there you go – octave. Sounds like something to do with music, but in reality it is the name the octave author’s chemical engineering professor, who was well know for his ‘back of the envelope’ calculations.

All of bench calculator!!!

All the operations of bench calculator are just a subset of octave. So, no point going round the wheel again. Whatever operations can be done in bc – arithmetic, logical, relational, conditional – all can be done with equal ease in octave. As in bc, it can as well be used as a mathematical programming language. Ha! Then, why did we waste time on learning bc, we could have straight away come to octave. Yes! Yes! Except one thing – the precision-less-ness. We can’t get that in octave. If we need that, we would have to go back to bc. Okay! Fine. So, what with octave – we start with N-dimensions, what else. Hey don’t worry! In simple language, I mean vectors & matrices.

Getting Started

Command: Typing ‘octave‘ on the shell brings up the octave‘s interactive shell. Type ‘quit’ or Control-D to exit. The interactive shell starts with a welcome message. As in bc, we pass option -q for it to not show. Additionally, we’ll use the -f option for it to not pick any local start-up scripts (if any). So, for our examples, the command would look like ‘octave -qf‘ to start octave with an interactive shell.

Prompts & Results: Input prompt is typically denoted by ‘octave:X>‘, where X just a number showing the command count. Valid results are typically shown with ‘<variable> = ‘, or ‘ans = ‘ or ‘=> ‘, and then the command count incremented for the next input. Errors cause error messages to be printed and then octave brings back the input prompt without incrementing the command count. Putting a semicolon (;) at the end of an statement, suppresses it result (return value) to be displayed.

Comments could start with # or %. For block comments, #{ … }# or %{ … }% can be used.

Detailed topic help could be obtained using ‘help <topic>’ or complete documentation using ‘doc’ on the octave shell.

All in a go:

$ octave -qf
octave:1> # This is a comment
octave:1> pi # Built-in constant
ans =  3.1416
octave:2> e # Built-in constant again
ans =  2.7183
octave:3> i # Same as j – the imaginary number
ans =  0 + 1i
octave:4> x = 3^4 + 2*30;
octave:5> x
x =  141
octave:6> y
error: `y' undefined near line 6 column 1
octave:6> doc # Complete doc; Press 'q' to come back
octave:7> help plot # Help on plot
octave:8> A = [1 2 3; 4 5 6; 7 8 9] # 3x3 matrix
A =

   1   2   3
   4   5   6
   7   8   9

octave:9> quit

Matrices with a Heart

Yes, we already saw a matrix in creation. It can also be created through multiple lines. Octave continues waiting for further input by prompting just ‘>’. Checkout below for creating the 3×3 magic square matrix in the same way, followed by various other interesting operations:

$ octave -qf
octave:1> M = [ # 3x3 magic square
> 8 1 6
> 3 5 7
> 4 9 2
> ]
M =

   8   1   6
   3   5   7
   4   9   2

octave:2> B = rand(3, 4); # 3x4 matrix w/ randoms in [0 1]
octave:3> B
B =

   0.068885   0.885998   0.542059   0.797678
   0.652617   0.904360   0.036035   0.737404
   0.043852   0.579838   0.709194   0.053118

octave:4> B' # Transpose of B
ans =

   0.068885   0.652617   0.043852
   0.885998   0.904360   0.579838
   0.542059   0.036035   0.709194
   0.797678   0.737404   0.053118

octave:5> A = inv(M) # Inverse of M
A =

   0.147222  -0.144444   0.063889
  -0.061111   0.022222   0.105556
  -0.019444   0.188889  -0.102778

octave:6> M * A # Should be identity, at least approx.
ans =

   1.00000   0.00000  -0.00000
  -0.00000   1.00000   0.00000
   0.00000   0.00000   1.00000

octave:7> function rv = psine(x) # Our phase shifted sine
>
> rv = sin(x + pi / 6);
>
>  endfunction 
octave:8> x = linspace(0, 2*pi, 400); # 400 pts from 0 to 2*pi
octave:9> plot(x, psine(x)) # Our function's plot
octave:10> polar(x, 10 * (1 - sin(x)), 'm*') # bonus Heart
octave:11> quit

Figure 1 shows the plot window which pops up by the command # 9 – plot.

Figure 2 is the bonus magenta heart of stars (*) from polar coordinates draw command # 10 – polar.

Figure 1: Plot of our 30° phase shifted sine

Figure 1: Plot of our 30° phase shifted sine

Figure 2: The Heart

Figure 2: The Heart

What next?

With ready-to-go level of introduction to octave, we are all set to explore it the fun way. What fun? Next one left to your imagination. And as we move on, we would take up one or more fun challenge(s), and try to see, how we solve it using octave.

Sixth Article >>

   Send article as PDF