Search code examples
clinux-kernellinux-device-driverdevice-driver

In Character Device Driver print statement executing infinite time in read method


I'm writing basic device driver module for character device driver. I want following behavior in that: When I will be reading from device file using cat /dev/scull, I should get, number of times the device is opened. For that I am using variable count and increasing it when my open function is called. I'm storing this variable in private structure. The problem I'm having is in read function

struct scull_dev{
    int x;              /*Private data members*/
    struct cdev cdev;   /*This is character device structure*/
};

ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    struct scull_dev *dev = filp->private_data;

    pr_info("You have opened device %d times\n",dev->x);
    return sizeof(dev->x);
}

The print statement "You have read device %d times" is printing infinite times. I have to press Ctr+D to stop it.

I want output to go to cat as well as in log file.


Solution

  • Note: you were getting infinite output because you were always returning sizeof. And, you had no code to copy data to cat.

    Okay, I've coded up what I think you'll need [please pardon some gratuitous style cleanup]. Note: I've not built it. I've annotated it a bit, so this should get you started:

    struct scull_dev {
        int x;                              /*Private data members*/
    
        int rdpend;                         // 1=partial read in progress
        int bufoff;                         // current offset within buffer
        int buflen;                         // remaining length to transfer
        char buf[100];                      // buffer with text to output
    
        struct cdev cdev;                   /*This is character device structure*/
    };
    
    ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
        loff_t *f_pos)
    {
        struct scull_dev *dev = filp->private_data;
        ssize_t curlen;
        long err;
    
        // NOTES:
        // (1) rdpend _must_ be cleared in the device close (and/or) device
        //     open -- we only want _one_ output line per invocation
        // (2) _after_ you get this working, you might move _this_ code block
        //     to the open routine (i.e. the rdpend would not be necessary)
        // (3) leaving it here makes the pr_info and cat output appear closer in
        //     time
        do {
            // only output a single line per open
            if (dev->rdpend)
                break;
            dev->rdpend = 1;
    
            dev->buflen = snprintf(dev->buf,sizeof(dev->buf),
                "You have opened device %d times\n",dev->x);
            pr_info("%s",dev->buf);
    
            dev->bufoff = 0;
        } while (0);
    
        // get length and handle user short read
        // [possibly less than we have--(e.g.) caller's count could be (say) 10
        curlen = dev->buflen;
        if (curlen > count)
            curlen = count;
    
        do {
            // nothing left to output
            if (curlen <= 0)
                break;
    
            err = copy_to_user(buf,dev->buf + dev->bufoff,curlen);
    
            // probably an access violation or segmentation fault, etc.
            if (err < 0) {
                curlen = err;
                break;
            }
    
            // creep through the buffer
            dev->buflen -= curlen;
            dev->bufoff += curlen;
        } while (0);
    
        return curlen;
    }
    

    Reminder: Don't forget to clear rdpend in your open and/or close.