Android cdev wrapper for block device

#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>

#define NR_HIDDEN_BYTES 16

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;

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);

offset = *ppos + NR_HIDDEN_BYTES;

nread = vfs_read(ptn_filp, buf, count, &offset);

if (nread < 0) {

mutex_unlock(&ptn_lock);

ptn_dbg("FATAL, ppos: %lld, offset: %lld, count: %ld, nread: %d\n",

*ppos, offset, count, nread);

return nread;

}

ptn_dbg("ppos: %lld, count: %ld, 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)

{

char kbuf[8];

loff_t offset;

int nwritten;

if (IS_ERR(ptn_filp)) {

return -EFAULT;

}

if (*ppos > g_ptn_size || (*ppos + count ) > g_ptn_size) {

return 0;

}

if (copy_from_user(kbuf, buf, count > 6 ? 6 : count)) {

ptn_dbg("failed to copy data from user\n");

}

mutex_lock(&ptn_lock);

if (!strncmp(kbuf, "ffbm-0", 6)) {

offset = *ppos;

} else {

offset = *ppos + NR_HIDDEN_BYTES;

}

nwritten = vfs_write(ptn_filp, buf, count, &offset);

if (nwritten < 0) {

mutex_unlock(&ptn_lock);

ptn_dbg("FATAL, ppos: %lld, offset: %lld, count: %ld, nwritten: %d\n",

*ppos, offset, count, nwritten);

return nwritten;

}

ptn_dbg("ppos: %lld, count: %ld, 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 - NR_HIDDEN_BYTES;

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<xxx@xxx.com>");

MODULE_DESCRIPTION("PTN character-device wrapper driver");

MODULE_LICENSE("GPL");

相关推荐
懒惰的毛毛虫1 年前
Linux 离线安装NFS共享文件
linux·服务器·centos·ufs·共享文件
jackailson1 年前
Linux MMC子系统 - 5.eMMC 5.1工作模式-引导模式
linux·嵌入式·emmc·linux内核与驱动·linux mmc子系统·mmc
jackailson1 年前
Linux MMC子系统 - 2.eMMC 5.1总线协议浅析
linux·嵌入式·emmc·linux内核与驱动·linux mmc子系统·mmc
jackailson1 年前
Linux MMC子系统 - 1.eMMC简介
linux·嵌入式·emmc·linux内核与驱动·linux mmc子系统·mmc
武汉万象奥科1 年前
RK3568核心板分区空间不足,如何修改分区大小?
3568·emmc
匈牙利认真的小菠萝1 年前
13.6 Production State Awareness (PSA)
ufs