简体   繁体   中英

Char Driver Linux: What is the correct implementation of file_operations read and write? What are the offset checks needs to be made?

I am trying to read and write to a char driver. When i use a C program to open the device file and read and write it gives a SEG fault. When I use a cat to the device file it goes to infinite loop.

1) What am I missing, and what is the correct implementation of read and write in file_operations?

2) I know in prototype read/write: read(struct file *fp, char *ch, size_t count, loff_t *lofft) count refers to number of bytes read/write request. But what is the last parameter offset used for and what are the checks needs to be made for offset?

3) For multiple read calls like cat /dev/chardriver will the offset be incremented for each read? Like 1st read call offset from 1 to 100 for count=100, in next read call will the offset go from 101? Or will it go from any random number?

Here is my code:

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/uaccess.h>

    static int device_open(struct inode *, struct file *);
    static int device_release(struct inode *, struct file *);
    static ssize_t device_read(struct file *, char *, size_t, loff_t *);
    static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
    char kernelbuff[1024];

    MODULE_LICENSE("GPL");

    struct file_operations fops = {
    .read = device_read,
    .write = device_write,
    .open = device_open,
    .release = device_release
    };


    int device_open(struct inode *inode, struct file *fp)
    {
    printk("device_open called");
    return 0;
    }

    static int device_release(struct inode *inode, struct file *fp)
    {
    printk("device_release called");
    return 0;
    }

    static ssize_t device_read(struct file *fp, char *ch, size_t sz, loff_t *lofft)
    {
    printk("device_read called");      
    copy_to_user(ch, kernelbuff, 1024);
    return sz;
    }

    static ssize_t device_write(struct file *fp, const char *ch, size_t sz, loff_t *lofft)
    {
    printk("device_write called");
    copy_from_user(kernelbuff, ch, 50);
    return 1024;
    }

    static int hello_init(void)
    {
      printk("basicchardriver: module initialized");
      register_chrdev(500, "chr_device", &fops);
      return 0;
    }

    static void hello_exit(void)
    {
      printk("basicchardriver: module exited");
      unregister_chrdev(500, "chr_device");
    }

    module_init(hello_init);
    module_exit(hello_exit);
 }

To test:

sudo mknod -m 666 /dev/chardev c 500 0
echo "Hello World" >> /dev/chardev    ===> Works fine
cat /dev/chardev     ===> Goes to infinite loop

If i call the driver using a C program it gives SEG fault:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

int main()
{
  int fd;
  char buff[500];

  fd = open("/dev/chardev", O_RDWR);
  write(fd, "Hello World", 13);

  read(fd, buff, 500);
  printf("Reading data from kernel: \t");
  puts(buff);

  return 0;
}

raj@raj-VirtualBox:~/device-driver/chardriver/read-write$ ./a.out
Reading data from kernel: Hello World
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)

I think i got the correct answer to my question: (experts please feel free to add your own answers /modify)

Here is the proper read which works:

static ssize_t device_read(struct file *fp, char *ch, size_t sz, loff_t *lofft)
{
        printk("device_read called");
        if (*lofft > 1024 || sz > 1024) 
        {
          return -EFBIF; // return 0 also works
        }
        if ((*lofft+sz) > 1024)
        {
           sz = 1024 - *lofft;
        }

        copy_to_user(ch, kernelbuff + *lofft, sz);
        *lofft+=sz;
        return sz;
}

(write operations code is below).

The answers to offset can be referred from here: Understanding loff_t *offp for file_operations

Some important points regarding offset:

  1. So, yes the offset of every consecutive read call needs to be adjusted in the read function.

  2. The next read offset should start from last_offset + count_of_no_of_bytes_read_in_last_function_call

  3. Read should return 0, if the offset exceeds the size of kernel buffer.

Edited: The valid checkings needed for read can be referred in this link (suggested by @Tsyvarev): Error checking in a '.read' function in kernel module

Edit: Adding the improved version of write function

static ssize_t device_write(struct file *fp, const char *ch, size_t sz, loff_t *lofft)
{
        printk("device_write called");
        if (((*lofft) > sizeof(kernelbuff)) || (sz > sizeof(kernelbuff)))
        {
        printk("Error: Allocating more than kernel buffer size"); // pr_err( ) can also be used as pointed by @KamilCuk
        return -EFBIG;
        }
        if ((*lofft + sz) > sizeof(kernelbuff))
        {
        printk("Error: Allocating more than kernel buffer size");
        return -EFBIG;
        }

        copy_from_user(kernelbuff + *lofft, ch, sz);
        *lofft+=sz;
        return sz;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM