cdev wrapper for block rw

#include <linux/blkdev.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/uaccess.h>

static bool debug = false;
module_param(debug, bool, 0644);

#define ptn_dbg(fmt, args...)                \
    do {                        \
        if (debug) {                \
            printk(KERN_DEBUG "[oem][%s] " fmt, \
                    __func__ , ## args);    \
        }                    \
    } while(0)

static int ptn_open_cnt = 0;
static loff_t g_ptn_size = 0;
static struct file *ptn_filp;
static struct kobject *ptn_kobj;
static unsigned char PTN_FILE[64] = {"/dev/block/mmcblk0p13"};
DEFINE_MUTEX(ptn_lock);

static loff_t ptn_llseek(struct file *file, loff_t offset, int origin)
{
    int ret = 0;

    mutex_lock(&ptn_lock);
    switch (origin) {
    case SEEK_SET:
        if (offset < 0 || (unsigned int) offset > g_ptn_size) {
            ret = -EINVAL;
            break;
        }

        file->f_pos = offset;
        ret = file->f_pos;
        break;
    case SEEK_CUR:
        if ((file->f_pos + offset) < 0 || (file->f_pos + offset) > g_ptn_size) {
            ret = -EINVAL;
            break;
        }

        file->f_pos += offset;
        ret = file->f_pos;
        break;
    case SEEK_END:
        offset += g_ptn_size;

        if (offset < 0 || offset > g_ptn_size) {
            ret = -EINVAL;
            break;
        }

        file->f_pos = offset;
        ret = file->f_pos;
        break;
    default:
        ret = -EINVAL;
    }
    mutex_unlock(&ptn_lock);

    return ret;
}

static ssize_t ptn_read(struct file *file, char __user *buf,
            size_t count, loff_t *ppos)
{
    loff_t offset = *ppos;
    unsigned nread = 0;

    if (IS_ERR(ptn_filp)) {
        return -EFAULT;
    }

    if (*ppos > g_ptn_size || (*ppos + count ) > g_ptn_size) {
        return 0;
    }

    mutex_lock(&ptn_lock);
    nread = vfs_read(ptn_filp, buf, count, &offset);
    if (nread < 0) {
        mutex_unlock(&ptn_lock);
        ptn_dbg("FATAL, ppos: %lld, offset: %lld, count: %d, nread: %d\n",
            *ppos, offset, count, nread);
        return nread;
    }

    ptn_dbg("ppos: %lld, count: %d, nread: %d\n", *ppos, count, nread);

    *ppos += nread;
    mutex_unlock(&ptn_lock);

    return nread;
}

static ssize_t ptn_write(struct file *file, const char __user *buf,
            size_t count, loff_t *ppos)
{
    loff_t offset = *ppos;
    int nwritten;

    if (IS_ERR(ptn_filp)) {
        return -EFAULT;
    }

    if (*ppos > g_ptn_size || (*ppos + count ) > g_ptn_size) {
        return 0;
    }

    mutex_lock(&ptn_lock);
    nwritten = vfs_write(ptn_filp, buf, count, &offset);
    if (nwritten < 0) {
        mutex_unlock(&ptn_lock);
        ptn_dbg("FATAL, ppos: %lld, offset: %lld, count: %d, nwritten: %d\n",
            *ppos, offset, count, nwritten);
        return nwritten;
    }

    ptn_dbg("ppos: %lld, count: %d, nwritten: %d\n", *ppos, count, nwritten);

    *ppos += nwritten;
    mutex_unlock(&ptn_lock);

    return nwritten;
}

static int ptn_open(struct inode *inode, struct file *file)
{
    struct inode *ptn_inode = NULL;
    loff_t size;
    int ro = 0;
    int rc = 0;

    if (ptn_open_cnt > 0) {
        return -EBUSY;
    }

    mutex_lock(&ptn_lock);
    ptn_open_cnt++;
    g_ptn_size = 0;

    ptn_filp = filp_open(PTN_FILE, O_RDWR | O_LARGEFILE, 0);
    if (PTR_ERR(ptn_filp) == -EROFS || PTR_ERR(ptn_filp) == -EACCES) {
        ro = 1;
    }

    if (ro) {
        ptn_filp = filp_open(PTN_FILE, O_RDONLY | O_LARGEFILE, 0);
    }
    if (IS_ERR(ptn_filp)) {
        mutex_unlock(&ptn_lock);
        ptn_dbg("unable to open backing file: %s\n", PTN_FILE);
        return PTR_ERR(ptn_filp);
    }

    ptn_inode = file_inode(ptn_filp);
    size = i_size_read(ptn_inode->i_mapping->host);
    if (size < 0) {
        ptn_dbg("unable to find file size: %s\n", PTN_FILE);
        rc = (int) size;
        goto drop_ptn_filp;
    }
    g_ptn_size = size;
    mutex_unlock(&ptn_lock);

    ptn_dbg("succeeded in opening the ptn file: %s, g_ptn_size: %lld\n",
        PTN_FILE, g_ptn_size);
    return 0;

drop_ptn_filp:
    if (ptn_filp) {
        filp_close(ptn_filp, NULL);
        ptn_filp = NULL;
    }
    mutex_unlock(&ptn_lock);

    return rc;
}

static int ptn_release(struct inode *inode, struct file *file)
{
    mutex_lock(&ptn_lock);

    if (ptn_open_cnt <= 0) {
        goto drop_ptn_filp;
    }

    ptn_open_cnt--;

    if (0 != ptn_open_cnt) {
        mutex_unlock(&ptn_lock);
        return 0;
    }

drop_ptn_filp:
    if (ptn_filp) {
        filp_close(ptn_filp, NULL);
        ptn_filp = NULL;
        ptn_dbg("succeeded in dropping the ptn file: %s\n", PTN_FILE);
    }
    mutex_unlock(&ptn_lock);

    return 0;
}

static const struct file_operations ptn_fops = {
    .owner        = THIS_MODULE,
    .llseek        = ptn_llseek,
    .read        = ptn_read,
    .write        = ptn_write,
    .open        = ptn_open,
    .release    = ptn_release,
};

static struct miscdevice ptn_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "misc-ptn",
    .fops = &ptn_fops
};

static ssize_t ptn_show(struct kobject  *obj,
            struct kobj_attribute *attr, char *buf)
{
    return snprintf(buf, PAGE_SIZE, "%s\n", PTN_FILE);
}

static ssize_t ptn_store(struct kobject *kobj, struct kobj_attribute *attr,
             const char *buf, size_t count)
{
    mutex_lock(&ptn_lock);
    strlcpy(PTN_FILE, buf, count);
    mutex_unlock(&ptn_lock);

    ptn_dbg("%s\n", PTN_FILE);

    return strlen(PTN_FILE) + 1;
}

static struct kobj_attribute ptn_obj_attr = {
    .attr = {
        .mode = S_IRUGO | S_IWUGO,
        .name = "blk-file",
    },
    .show = ptn_show,
    .store = ptn_store,
};

static struct attribute *ptn_attr[] = {
    &ptn_obj_attr.attr,
    NULL,
};

static struct attribute_group ptn_grp = {
    .attrs = ptn_attr,
};

static int ptn_add_sys_fs(void)
{
    int rc;

    ptn_kobj = kobject_create_and_add("misc-ptn", NULL);
    if (!ptn_kobj) {
        ptn_dbg("unable to create kobject\n");
        return -ENOMEM;
    }

    rc = sysfs_create_group(ptn_kobj, &ptn_grp);
    if (rc) {
        ptn_dbg("failed to create attributes\n");
        kobject_put(ptn_kobj);
        return -ENOENT;
    }

    return rc;
}

static int __init ptn_init(void)
{
    int ret;

    ret = misc_register(&ptn_dev);
    if (ret) {
        ptn_dbg("can't misc_register\n");
        goto out;
    }

    ret = ptn_add_sys_fs();
    if (ret) {
        ptn_dbg("can't create /sys/misc-ptn/blk-file\n");
    }
out:
    return ret;
}

static void __exit ptn_exit(void)
{
    misc_deregister(&ptn_dev);
}

module_init(ptn_init);
module_exit(ptn_exit);

MODULE_AUTHOR("George Tso <[email protected]>");
MODULE_DESCRIPTION("PTN character-device wrapper driver");
MODULE_LICENSE("GPL");


你可能感兴趣的:(cdev wrapper for block rw)