gaston
gaston

Reputation: 425

Linux kernel module : Is it possible to use an open function inside another open function for my module?

Maybe this question makes no sense, but I was wondering if there was a "recommended practice" on how to open a file descriptor for a device inside an open function of the created module.

In fact, I developped a simple Linux kernel module with its basic functions :

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

MODULE_LICENSE("GPL");      
MODULE_AUTHOR("Gaston");  
MODULE_DESCRIPTION("A simple Linux char driver"); 
MODULE_VERSION("0.1"); 


ssize_t exer_open(struct inode *pinode, struct file *pfile) {

    printk(KERN_INFO "Device has been opened\n");

    return 0;
}



ssize_t exer_read(struct file *pfile, char __user *buffer, size_t length, loff_t *offset) {

    return 0;
}



ssize_t exer_write(struct file *pfile, const char __user *buffer, size_t length, loff_t *offset) {

    return 0;

}   




ssize_t exer_close(struct inode *pinode, struct file *pfile) {

    printk(KERN_INFO "Device successfully closed\n");
    return 0;
}


struct file_operations exer_file_operations = { 
    .owner = THIS_MODULE,
    .open = exer_open,
    .read = exer_read,
    .write = exer_write,
    .release = exer_close,
};


int exer_simple_module_init(void) {

    printk(KERN_INFO "Initializing the LKM\n");
    register_chrdev(240, "Simple Char Drv", &exer_file_operations);
    return 0;
}


void exer_simple_module_exit(void) {

    unregister_chrdev(240, "Simple Char Drv");
}


module_init(exer_simple_module_init);
module_exit(exer_simple_module_exit);

I compile it and no errors occured.

Now I want to open the file descriptor of my device ( BUTTON ) in order to manipulate it later from user space program, so I made some modifications by adding the BUTTON device path and another open function like this :

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

MODULE_LICENSE("GPL");      
MODULE_AUTHOR("Gaston");  
MODULE_DESCRIPTION("A simple Linux char driver"); 
MODULE_VERSION("0.1"); 

#define BTN_FILE_PATH "/dev/input/event0"

int file;
char *str = BTN_FILE_PATH;


ssize_t exer_open(struct inode *pinode, struct file *pfile) {

    printk(KERN_INFO "Device has been opened\n");

    if((file = open(str, O_RDONLY)) < 0) {
        printk("simplekey: File can not open");
        return(-1);
    }
    return 0;
}



ssize_t exer_read(struct file *pfile, char __user *buffer, size_t length, loff_t *offset) {

    return 0;
}


ssize_t exer_write(struct file *pfile, const char __user *buffer, size_t length, loff_t *offset) {

    return 0;

}   


ssize_t exer_close(struct inode *pinode, struct file *pfile) {

    printk(KERN_INFO "Device successfully closed\n");
    return 0;
}


struct file_operations exer_file_operations = { 
    .owner = THIS_MODULE,
    .open = exer_open,
    .read = exer_read,
    .write = exer_write,
    .release = exer_close,
};


int exer_simple_module_init(void) {

    printk(KERN_INFO "Initializing the LKM\n");
    register_chrdev(240, "Simple Char Drv", &exer_file_operations);
    return 0;
}


void exer_simple_module_exit(void) {

    unregister_chrdev(240, "Simple Char Drv");
}


module_init(exer_simple_module_init);
module_exit(exer_simple_module_exit);

But the problem, when I try to compile the module now errors are printed :

/home/gaston/ledshared/exer_simple_char_drv.c: In function ‘exer_open’: /home/gaston/ledshared/exer_simple_char_drv.c:32:13: error: implicit declaration of function ‘open’ [-Werror=implicit-function-declaration]

if((file = open(str,O_RDONLY)) < 0) {

How can I fix the problem please ?

Upvotes: 0

Views: 2086

Answers (1)

Ian Abbott
Ian Abbott

Reputation: 17403

open() is a user-space function. The equivalent kernel-space function is filp_open(), but it returns a struct file * instead of a int file descriptor. The returned struct file * could be an error code instead of a valid pointer. Use the IS_ERR(ptr) macro to check for that, and the PTR_ERR(ptr) macro to extract the error code (which will be a negated errno value).

Use of the filp_open function is discouraged, but here are some modifications to your code to use this function:

int exer_open(struct inode *pinode, struct file *pfile) {
    struct file *f;

    f = filp_open(str, O_RDONLY);
    if (IS_ERR(f)) {
        printk("simplekey: File can not open");
        return(PTR_ERR(f));
    }
    pfile->private_data = f;

    printk(KERN_INFO "Device has been opened\n");
    return 0;
}

The close function should look something like this:

int exer_close(struct inode *pinode, struct file *pfile) {
    struct file *f = pfile->private_data;
    int rc;

    rc = filp_close(f, NULL);
    if (rc == 0) {
        printk(KERN_INFO "Device successfully closed\n");
    }
    return rc;
}

There is no legitimate way for a module to read from a struct file * directly into a user-space buffer or write from a user-space buffer to a struct file *, so an intermediate buffer in kernel memory is needed, so that kernel_read() or kernel_write() can be used to read or write the file:

ssize_t exer_read(struct file *pfile, char __user *buffer, size_t length, loff_t *offset) {
    struct file *f = pfile->private_data;
    enum { MAX_BUF_SIZE = 4096 };
    size_t buf_size = 0;
    char *buf = NULL;
    ssize_t total = 0;
    ssize_t rc = 0;

    /* Allocate temporary buffer. */
    if (length) {
        buf_size = min_t(size_t, MAX_BUF_SIZE, length);
        buf = kmalloc(buf_size, GFP_KERNEL);
        if (buf == NULL) {
            return -ENOMEM;
        }
    }

    /* Read file to buffer in chunks. */
    do {
        size_t amount = min_t(size_t, length, buf_size);

        rc = kernel_read(f, buf, amount, offset);
        if (rc > 0) {
            /* Have read some data from file. */
            if (copy_to_user(buffer, buf, rc) != 0) {
                /* Bad user memory! */
                rc = -EFAULT;
            } else {
                /* Update totals. */
                total += rc;
                buffer += rc;
                *offset += rc;
                length -= rc;
                if (rc < amount) {
                    /* Didn't read the full amount, so terminate early. */
                    rc = 0;
                }
            }
        }
    } while (rc > 0 && length > 0);

    /* Free temporary buffer. */
    kfree(buf);

    if (total > 0) {
        return total;
    }
    return rc;
}


ssize_t exer_write(struct file *pfile, const char __user *buffer, size_t length, loff_t *offset) {
    struct file *f = pfile->private_data;
    enum { MAX_BUF_SIZE = 4096 };
    size_t buf_size = 0;
    char *buf = NULL;
    ssize_t total = 0;
    ssize_t rc = 0;

    /* Allocate temporary buffer. */
    if (length) {
        buf_size = min_t(size_t, MAX_BUF_SIZE, length);
        buf = kmalloc(buf_size, GFP_KERNEL);
        if (buf == NULL) {
            return -ENOMEM;
        }
    }

    /* Write file from buffer in chunks. */
    do {
        size_t amount = min_t(size_t, length, buf_size);

        if (copy_from_user(buf, buffer, amount) != 0) {
            /* Bad user memory! */
            rc = -EFAULT;
        } else {
            rc = kernel_write(f, buf, amount, offset);
            if (rc > 0) {
                /* Have written some data to file. */
                /* Update totals. */
                total += rc;
                buffer += rc;
                *offset += rc;
                length -= rc;
                if (rc < amount) {
                    /* Didn't write the full amount, so terminate early. */
                    rc = 0;
                }
            }
        }
    } while (rc > 0 && length > 0);

    /* Free temporary buffer. */
    kfree(buf);

    if (total > 0) {
        return total;
    }
    return rc;
}   

Upvotes: 1

Related Questions