Menu
Linux Character Device Example. GitHub Gist: instantly share code, notes, and snippets. This article is a continuation of the series on Linux device drivers, and carries on the discussion on character drivers and their implementation. 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.
The file structure Each device is represented in the kernel by a file structure, which is defined in linux/fs.h. Be aware that a file is a kernel level structure and never appears in a user space program.
It's not the same thing as a FILE, which is defined by glibc and would never appear in a kernel space function. Also, its name is a bit misleading; it represents an abstract open `file', not a file on a disk, which is represented by a structure named inode. An instance of struct file is commonly named filp. You'll also see it refered to as struct file file. Resist the temptation. Go ahead and look at the definition of file.
Most of the entries you see, like struct dentry aren't used by device drivers, and you can ignore them. This is because drivers don't fill file directly; they only use structures contained in file which are created elsewhere.
![Character Character](/uploads/1/2/5/5/125582814/198827411.jpg)
Registering A Device As discussed earlier, char devices are accessed through device files, usually located in /dev. The major number tells you which driver handles which device file. The minor number is used only by the driver itself to differentiate which device it's operating on, just in case the driver handles more than one device. Adding a driver to your system means registering it with the kernel. This is synonymous with assigning it a major number during the module's initialization. You do this by using the registerchrdev function, defined by linux/fs.h.
Int registerchrdev(unsigned int major, const char.name, struct fileoperations.fops); where unsigned int major is the major number you want to request, const char.name is the name of the device as it'll appear in /proc/devices and struct fileoperations.fops is a pointer to the fileoperations table for your driver. A negative return value means the registration failed. Note that we didn't pass the minor number to registerchrdev. That's because the kernel doesn't care about the minor number; only our driver uses it. Now the question is, how do you get a major number without hijacking one that's already in use? The easiest way would be to look through Documentation/devices.txt and pick an unused one. That's a bad way of doing things because you'll never be sure if the number you picked will be assigned later.
The answer is that you can ask the kernel to assign you a dynamic major number. If you pass a major number of 0 to registerchrdev, the return value will be the dynamically allocated major number. The downside is that you can't make a device file in advance, since you don't know what the major number will be. There are a couple of ways to do this. First, the driver itself can print the newly assigned number and we can make the device file by hand. Second, the newly registered device will have an entry in /proc/devices, and we can either make the device file by hand or write a shell script to read the file in and make the device file.
The third method is we can have our driver make the the device file using the mknod system call after a successful registration and rm during the call to cleanupmodule. Unregistering A Device We can't allow the kernel module to be rmmod'ed whenever root feels like it. If the device file is opened by a process and then we remove the kernel module, using the file would cause a call to the memory location where the appropriate function (read/write) used to be. If we're lucky, no other code was loaded there, and we'll get an ugly error message.
If we're unlucky, another kernel module was loaded into the same location, which means a jump into the middle of another function within the kernel. The results of this would be impossible to predict, but they can't be very positive. Normally, when you don't want to allow something, you return an error code (a negative number) from the function which is supposed to do it. With cleanupmodule that's impossible because it's a void function. However, there's a counter which keeps track of how many processes are using your module. You can see what it's value is by looking at the 3rd field of /proc/modules.
If this number isn't zero, rmmod will fail. Note that you don't have to check the counter from within cleanupmodule because the check will be performed for you by the system call sysdeletemodule, defined in linux/module.c. You shouldn't use this counter directly, but there are functions defined in linux/module.h which let you increase, decrease and display this counter:. trymoduleget(THISMODULE): Increment the use count. moduleput(THISMODULE): Decrement the use count.
It's important to keep the counter accurate; if you ever do lose track of the correct usage count, you'll never be able to unload the module; it's now reboot time, boys and girls. This is bound to happen to you sooner or later during a module's development. Chardev.c The next code sample creates a char driver named chardev. You can cat its device file (or open the file with a program) and the driver will put the number of times the device file has been read from into the file. We don't support writing to the file (like echo 'hi' /dev/hello), but catch these attempts and tell the user that the operation isn't supported. Don't worry if you don't see what we do with the data we read into the buffer; we don't do much with it.
We simply read in the data and print a message acknowledging that we received it.