Creating Null Character device /dev/my_null
Theory:
Connection between the application and the device file is based on the name of the device file. However the connection between the device file and the device driver is based on the number of the device file, not the name.
Important Structures:
struct file_operations:
Defined in : linux/fs.h
Purpose: Holds pointers to functions defined by the driver that performs various operations on the device.
E.g.
struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
The above fops structure has defined four function pointers : For reading, writing, opening and closing the device file
struct inode: An inode uniquely identifies a file in a file system. Attributes of an inode are
- Size
- Rights
- Times associated with the file
struct file: Created by kernel on open and is passed to any function that operates on the file, until the last close. After all instances of the file are closed, the kernel releases the data structure. An open file is different from a disk file, represented by struct inode.
Contains many fields:
- f_mode (Read or Write)
- f_flags, File opening flags (O_RDONLY, O_NONBLOCK, O_SYNC, O_APPEND, O_TRUNC, etc.)
- f_op, operations associated with the file
- private_data, pointer that can be used by the programmer to store device-specific data. The pointer will be initialized to a memory location assigned by the programmer
- f_pos, the offset within a file.
struct cdev : In kernel, each character device is represented using this structure.
Steps in creating a character driver:
1. Allocate a device number dynamically or statically
2. Initializing the character device with its file operations
3. Registering the character device with Linux Kernel
Code for Null Device which eats everything:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <linux/kernel.h> | |
#include <linux/module.h> | |
#include <linux/kdev_t.h> | |
#include <linux/types.h> | |
#include <linux/fs.h> | |
#include <linux/cdev.h> | |
MODULE_LICENSE("GPL"); | |
dev_t device_number; | |
bool dynamic = true; | |
static struct cdev my_cdev; | |
static int mydevice_open(struct inode *inode, struct file *file) | |
{ | |
pr_info("%s\n", __func__); | |
return 0; | |
} | |
static int mydevice_release(struct inode *inode, struct file *file) | |
{ | |
pr_info("%s\n", __func__); | |
return 0; | |
} | |
ssize_t mydevice_read(struct file *file, char __user *user_buffer, | |
size_t count, loff_t *offset) | |
{ | |
pr_info("%s\n", __func__); | |
return 0; | |
} | |
ssize_t mydevice_write(struct file *file, const char __user *user_buffer, | |
size_t count, loff_t *offset) | |
{ | |
pr_info("%s\n", __func__); | |
return count; | |
} | |
struct file_operations fops = { | |
.owner = THIS_MODULE, | |
.open = mydevice_open, | |
.release = mydevice_release, | |
.read = mydevice_read, | |
.write = mydevice_write | |
}; | |
static int mynull_device_init(void) | |
{ | |
int retval; | |
pr_info("%s: In init\n", __func__); | |
if (dynamic) { | |
retval = alloc_chrdev_region(&device_number, 0, 1, "embedded"); | |
} | |
else { | |
device_number = MKDEV(180, 0); | |
retval = register_chrdev_region(device_number, 1, "embedded"); | |
} | |
if (!retval) { | |
pr_info("%s: Major Number:%d\t Minor Number:%d\n", | |
__func__, MAJOR(device_number), MINOR(device_number)); | |
cdev_init(&my_cdev, &fops); | |
retval = cdev_add(&my_cdev, device_number, 1); | |
if (retval) | |
pr_info("%s: Failed in adding cdev to subsystem " | |
"retval:%d\n", __func__, retval); | |
} | |
else | |
pr_err("%s: Failed in allocating device number " | |
"Error:%d\n", __func__, retval); | |
return retval; | |
} | |
static void mynull_device_exit(void) | |
{ | |
cdev_del(&my_cdev); | |
unregister_chrdev_region(device_number, 1); | |
pr_info("%s: In exit\n", __func__); | |
} | |
module_init(mynull_device_init); | |
module_exit(mynull_device_exit); |
Output:
Notes:
1. You can refer to the original /dev/null code in drivers/char/mem.c . This same code is for /dev/zero and all other nodes whose major number is 1
2. You can also try to see what happens when you type 'cat /dev/zero > /dev/my_null' (dmesg)
1. You can refer to the original /dev/null code in drivers/char/mem.c . This same code is for /dev/zero and all other nodes whose major number is 1
2. You can also try to see what happens when you type 'cat /dev/zero > /dev/my_null' (dmesg)
Comments
Post a Comment