Before reading this article, you must read Stage 1 of this article series.
The stage is going to contain:
1. How to register the driver with the kernel. I will also talk about what is the difference between registering a driver and loading or inserting a driver.
2. How to call registered functions
3. How to access device file from user space
Please look at the code below:
/* File name is fld.c. * The file has four key concepts here: * * 1. Registering the driver with kernel. For that we have used alloc_chrdev_region. * This API gives us device id which have a major number and minor number of devices. * There are many times where we need more than one device file and using this API we have. * i.e. tty. We will use the device ID to create 3 device file which we can see using * $ ls -l /dev/ * * 2. class_create - create class for all devices under /sys/class * * 3. device_create - create all device under /sys/class/[our class] * * 4. cdev_init and cdev_add : VFS s reponsible to handle all our device file operation. * the api help to register all functions under file_operations and * cdev_init initialize the device structure while hand over this structure to * VFS by calling cdev_add */ #include /* All module related APIs are declared */ #include /* declares all character device related functions*/ #include /* for copy_to_user and copy_from_user*/ #include /* kmalloc() */
static dev_t device_id; static struct class *device_class; static struct cdev c_dev; /* Buffer to store data */ char *memory_buffer; static int fld_open(struct inode *inodePtr, struct file *filePtr) { printk(KERN_INFO "FLD Driver: open()\n"); return 0; } static int fld_close(struct inode *inodePtr, struct file *filePtr) { printk(KERN_INFO "FLD Driver: close()\n"); return 0; } static ssize_t fld_read(struct file *filePtr, char __user *buf, size_t length, loff_t *f_pos) { /* Transfering data to user space */ copy_to_user(buf, memory_buffer, 1) if (*f_pos == 0) /* Changing reading position as best suits */ { *f_pos += 1; return 1; } else return 0; } static ssize_t fld_write(struct file *filePtr, const char __user *buf, size_t length, loff_t *off) { const char *tmp; tmp = buf + length - 1; copy_from_user(memory_buffer, tmp, 1); return 1; } static void __exit fld_exit(void) { printk(KERN_INFO "FLD Exit Called"); cdev_del(&c_dev); if (memory_buffer) { kfree(memory_buffer); } device_destroy(device_class, device_id); class_destroy(device_class); unregister_chrdev_region(device_id, 1); }
static struct file_operations fld_fops = { .owner = THIS_MODULE, .open = fld_open, .release = fld_close, .read = fld_read, .write = fld_write}; /* for '__init'/'__exit' - find info in stage 1 article*/ static int __init fld_init(void) { int ret = 0; printk(KERN_INFO "FLD Init Called"); /* The below API dynamically figures out a free major number and * registers the cnt number of device file numbers starting from * , with the name. */ /* Registers a range of char device numbers */ /* When you load driver and run the command 'cat /proc/devices' * you can see the device file name as 'fld_device' for the driver */ if ((ret = alloc_chrdev_region(&device_id, 0, 1, "fld_device")) < 0) { return ret; } /* Allocating memory for the buffer */ memory_buffer = kmalloc(1, GFP_KERNEL); if (!memory_buffer) { ret = -ENOMEM; goto fail; } memset(memory_buffer, 0, 1); /* Kernel populates the appropriate device class & device info into * the '$ ls -l /sys/class/' you could see 'fld_class' */ /* Create device class */ if ((device_class = class_create(THIS_MODULE, "fld_class")) == NULL) { unregister_chrdev_region(device_id, 1); return -1; } /* Create device info */ /* when you execute $ ls -l /sys/class/fld_class/', you can see device 'fld_dev' */ if (device_create(device_class, NULL, device_id, NULL, "fld_dev") == NULL) { class_destroy(device_class); unregister_chrdev_region(device_id, 1); return -1; } /* Initialize the character device structure (struct cdev c_dev) * with that, using cdev_init()*/ cdev_init(&c_dev, &fld_fops); /* to hand this structure to the VFS using the call cdev_add() */ if (cdev_add(&c_dev, device_id, 1) == -1) { device_destroy(device_class, device_id); class_destroy(device_class); unregister_chrdev_region(device_id, 1); return -1; } return 0; fail: fld_exit(); return ret; } /* Find info about module_init and module_exit in Stage 1 article code*/ module_init(fld_init); module_exit(fld_exit); /* Refer stage 1 article for explanation below. */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mukesh Krishna"); MODULE_DESCRIPTION("For self-learning LDD"); |
Refer to stage 1 article.
Refer to stage 1 article.
cat is a command which opens a file, reads it,and then closes the file.
We will use this command to open and read our driver which is a device file.
To see the major and minor number, run the following command:
$ cat /sys/class/fld_class/fld_dev/uevent |
$ cat /dev/fld_dev |
To Write
echo -n First > /dev/fld_dev |
See all class file i.e. fld_class
$ ls -l /sys/class/ |
See device file i.e. fld_dev
$ ls -l /sys/class/fld_class/ $ ls -l /dev/ |
See Loaded driver
$ lsmod |