0

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
  • @Ameermon: You correctly adjusts return value of `.read` function, but do not process its `off` argument. Without that processing it is impossible to correctly implement the function. See more in the referenced question and its answers. – Tsyvarev Aug 02 '18 at 07:26
  • @Tsyvarev , Thanks for your solution too. – Ameermon V A Aug 04 '18 at 01:00

0 Answers0