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:


#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);
view raw jiffie_device.c hosted with ❤ by GitHub


Test Application:


#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);
}
view raw testapp.c hosted with ❤ by GitHub


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

Popular posts from this blog

bb.utils.contains yocto

Difference between RDEPENDS and DEPENDS in Yocto

PR, PN and PV Variable in Yocto