Creating jiffie character device - /dev/jiffies
What is jiffies?
Informally, According to wikipedia, jiffy is term used for any unspecified short period of time.
Coming back to our Linux Kernel World, jiffies is a global variable which stores the number of clock ticks since boot. It is present in <linux/jiffies.h>
Let's write a character driver which on read returns the value of 'jiffies'.
Code:
Test Application:
Output:
Informally, According to wikipedia, jiffy is term used for any unspecified short period of time.
Coming back to our Linux Kernel World, jiffies is a global variable which stores the number of clock ticks since boot. It is present in <linux/jiffies.h>
Let's write a character driver which on read returns the value of 'jiffies'.
Code:
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> | |
#include <linux/sched.h> | |
#include <linux/uaccess.h> | |
MODULE_LICENSE("GPL"); | |
dev_t device_number; | |
bool dynamic = true; | |
struct class *my_class; | |
static struct cdev my_cdev; | |
static int jiffiedevice_open(struct inode *inode, struct file *file) | |
{ | |
pr_info("%s\n", __func__); | |
return 0; | |
} | |
static int jiffiedevice_release(struct inode *inode, struct file *file) | |
{ | |
pr_info("%s\n", __func__); | |
return 0; | |
} | |
ssize_t jiffiedevice_read(struct file *file, char __user *user_buffer, | |
size_t count, loff_t *offset) | |
{ | |
pr_info("%s\n", __func__); | |
if (count < sizeof(jiffies)) | |
return -EINVAL; | |
if (!put_user(jiffies, (u64 *)user_buffer)) | |
return sizeof(jiffies); | |
else | |
return -EFAULT; | |
} | |
ssize_t jiffiedevice_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 = jiffiedevice_open, | |
.release = jiffiedevice_release, | |
.read = jiffiedevice_read, | |
.write = jiffiedevice_write | |
}; | |
static int jiffie_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)); | |
my_class = class_create(THIS_MODULE, "my_driver_class"); | |
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 { | |
device_create(my_class, NULL, device_number, NULL, "jiffies"); | |
} | |
} | |
else | |
pr_err("%s: Failed in allocating device number " | |
"Error:%d\n", __func__, retval); | |
return retval; | |
} | |
static void jiffie_device_exit(void) | |
{ | |
cdev_del(&my_cdev); | |
device_destroy(my_class, device_number); | |
class_destroy(my_class); | |
unregister_chrdev_region(device_number, 1); | |
pr_info("%s: In exit\n", __func__); | |
} | |
module_init(jiffie_device_init); | |
module_exit(jiffie_device_exit); |
Test Application:
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 <stdio.h> | |
#include <fcntl.h> | |
#include <stdlib.h> | |
int main(int argc, char *argv[]) | |
{ | |
int fd; | |
unsigned long long old_jiffies; | |
unsigned long long new_jiffies; | |
fd = open("/dev/jiffies", O_RDWR); | |
if (fd < 0) { | |
perror("fd failed"); | |
exit(2); | |
} | |
if (read(fd, &old_jiffies, sizeof(old_jiffies)) != sizeof(old_jiffies)) { | |
printf("Failed in reading first jiffies\n"); | |
exit(3); | |
} else { | |
printf("First Read:%lld\n", old_jiffies); | |
} | |
sleep(1); | |
if (read(fd, &new_jiffies, sizeof(new_jiffies)) != sizeof(new_jiffies)) { | |
printf("Failed in reading second jiffies\n"); | |
exit(4); | |
} else { | |
printf("Second Read:%lld\n", new_jiffies); | |
} | |
printf("Difference:%lld\n", (new_jiffies - old_jiffies)); | |
close(fd); | |
} |
Output:
Notes:
1. You can see from the output, the difference we got is 250, it means the value of jiffie increments by 250 every second, this also means the timer is configured to generate 250 interrupts every second.
2. We use put_user to copy the value of jiffies to user space. put_user is faster than copy_to_user, and can copy up to 8 bytes of data. The size that put_user copies depends on the type of the pointer argument and is determined at compiled time using typeof and sizeof builtins.
Comments
Post a Comment