I was studying basic linux kernel modules (character device driver) with file_operations structure. I have implemented open, release, read, write methods in it. All methods are working as expected except read.
The driver which I wrote will reverse the string written on the associated device node (echo Hello /dev/rev). Once I read back, it should return the reversed string (cat /dev/rev). The output should be "olleH".
Issue: When I am reading (cat /dev/rev), the read routine in the driver is getting called infinite times. So I am getting continuous output as below:
olleH
olleH
olleH
olleH
..
I have searched, but I couldn't find an answer to it. So please suggest me what I am doing wrong here.
Code: reverse.c
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#define MYDRIVER "revese"
static int major = 0;
static int open_cnt = 0;
static char kbuf[100];
static int myDev_open(struct inode *in, struct file *filp)
{
open_cnt++;
printk(KERN_INFO "myDev_open: open_cnt = %d\n", open_cnt);
return 0;
}
static int myDev_close (struct inode *in, struct file *filp)
{
if(open_cnt > 0)
open_cnt--;
printk(KERN_INFO "myDev_close:\n");
return 0;
}
static ssize_t myDev_read(struct file *filp, char __user *buf, size_t n, loff_t *off)
{
int i;
printk("n from the userspace = %ld\n", n);
for(i = 0; i < n && kbuf[i] != 0; ++i) {
put_user(kbuf[i], buf++);
}
*buf = '\0';
printk(KERN_INFO "myDev_read: read length = %d; buf = %s\n", i, buf-i);
return i;
}
static ssize_t myDev_write(struct file *filp, const char __user *buf, size_t n, loff_t *off)
{
int i, ind;
printk(KERN_INFO "myDev_write: n = %ld\n", n);
memset(kbuf, 0, 100);
for(i = 0, ind = (n-1); i < n; ++i, --ind) {
kbuf[i] = buf[ind];
}
kbuf[i] = '\0';
return i;
}
static struct file_operations file_ops = {
.owner = THIS_MODULE,
.open = myDev_open,
.release = myDev_close,
.read = myDev_read,
.write = myDev_write,
};
static int __init myDev_init(void)
{
int rval;
printk(KERN_INFO "myDev_init: \n");
rval = register_chrdev(0, MYDRIVER, &file_ops);
if(rval < 0) {
printk(KERN_ERR "myDev_init: driver registration failed\n");
return rval;
}
major = rval;
printk(KERN_INFO "myDev_init: driver reg success, major number = %d\n", major);
return 0;
}
static void __exit myDev_exit(void)
{
printk(KERN_INFO "myDev_exit: \n");
unregister_chrdev(major, MYDRIVER);
}
module_init(myDev_init);
module_exit(myDev_exit);
Makefile:
TARGET_MODULE := reverse
BUILDSYSTEM_DIR := '/lib/modules/$(shell uname -r)/build'
PWD := $(shell pwd)
obj-m := $(TARGET_MODULE).o
defult:
$(MAKE) -C $(BUILDSYSTEM_DIR) SUBDIRS=$(PWD) modules
clean:
$(MAKE) -C $(BUILDSYSTEM_DIR) SUBDIRS=$(PWD) clean
Commands:
sudo insmod reverse.ko
sudo mknod /dev/rev c 250 0
sudo chmod a+w+r /dev/rev
echo Hello > /dev/rev
cat /dev/rev