blob: 35ccb8190159b54aaec7d80b4e01fa82da059936 [file] [log] [blame]
/*
* rpmsg write to nfs server driver
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rpmsg.h>
#include <linux/err.h>
#include <linux/remoteproc.h>
#include <linux/poll.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/kfifo.h>
#include <linux/virtio_ring.h>
#include <linux/sched.h>
#include <linux/delay.h>
#define DEVICE_NAME "wnfs"
typedef int redev_nfs_FILE;
typedef struct nfs_fwrite_info_s
{
int* addr;
int size;
int count;
redev_nfs_FILE *file;
}
nfs_fwrite_info_t;
struct wnfs_struct {
struct kfifo fifo;
spinlock_t lock;
struct rpmsg_channel *rpdev;
struct miscdevice fdev;
nfs_fwrite_info_t unfinished;
};
static struct wnfs_struct g_wnfs;
static int device_open(struct inode *inode, struct file *filp)
{
filp->private_data = &g_wnfs;
return 0;
}
static ssize_t device_write(struct file *filp, const char *buf,
size_t count, loff_t * off)
{
struct wnfs_struct *wnfs = filp->private_data;
struct rpmsg_channel *rpdev = wnfs->rpdev;
rpmsg_sendto(rpdev, (void *)buf, count, rpdev->dst);
return count;
}
int wnfs_test_counter = 0;
static ssize_t device_read(struct file *filp, char *buf,
size_t len, loff_t * offset)
{
struct wnfs_struct *wnfs = filp->private_data;
int bytes_read = 0;
// char c;
char *vaddr;
nfs_fwrite_info_t nfs_fwrite_info;
int copy_size;
int unfinished_size;
int ret;
int len_remain = len;
//printk(" ====================== device_read %d ======================\n", wnfs_test_counter);
//wnfs_test_counter ++;
if(wnfs_test_counter > 2000)
{
printk("device_read: force 0\n");
wnfs_test_counter = 0;
return 0;
}
// while (kfifo_out_locked(&wnfs->fifo, &c, sizeof(c), &wnfs->lock)) {
//
// *buf++ = c;
// bytes_read++;
// }
// blocking = filp->??
while(len_remain > 0) {
switch (wnfs->unfinished.count) {
case 0:
//reload
// while(1) {
ret = kfifo_out_locked(&wnfs->fifo, &nfs_fwrite_info, sizeof(nfs_fwrite_info_t), &wnfs->lock);
//printk("reload: info %d/%d\n", ret, sizeof(nfs_fwrite_info_t));
// //if ret==not ready, sleep
// if(ret < sizeof(nfs_fwrite_info_t))
// if(blocking)
// sleep();
// else return -EAGAIN;
// else
// break;
// }
if(ret < sizeof(nfs_fwrite_info_t)) {
msleep(5);
//printk("reload: not ready %d\n", ret);
break;
} else {
if(nfs_fwrite_info.size == 0xdeadbeef) {
printk("nfs_fwrite_info.size == 0xdeadbeef");
wnfs_test_counter = 2000000;
rpmsg_sendto(wnfs->rpdev, &bytes_read, sizeof(int), wnfs->rpdev->dst); //ack to release fread of iTRON side
return 0;
}
memcpy(&(wnfs->unfinished), &nfs_fwrite_info, sizeof(nfs_fwrite_info_t));
//printk("reload: x%x %d %d\n", wnfs->unfinished.addr, wnfs->unfinished.size, wnfs->unfinished.count);
}
default:
//fill-up buf
unfinished_size = wnfs->unfinished.size * wnfs->unfinished.count;
copy_size = (len_remain > unfinished_size) ? unfinished_size : len_remain;
if(copy_size>0)
{
vaddr = (void*)ambarella_phys_to_virt((unsigned long)wnfs->unfinished.addr);
memcpy(buf, vaddr, copy_size);
bytes_read += copy_size;
buf += copy_size;
unfinished_size -= copy_size;
if(unfinished_size > 0) {
wnfs->unfinished.count = unfinished_size / wnfs->unfinished.size;
wnfs->unfinished.addr = (int*)((unsigned int)wnfs->unfinished.addr + copy_size);
} else {
memset(&(wnfs->unfinished), 0, sizeof(nfs_fwrite_info_t));
rpmsg_sendto(wnfs->rpdev, &bytes_read, sizeof(int), wnfs->rpdev->dst); //ack to release fread of iTRON side
//printk("ack\n");
}
len_remain -= copy_size;
}
//printk("fillup: %d/%d \n", bytes_read, len);
}
}
// while (kfifo_out_locked(&wnfs->fifo, &nfs_fwrite_info, sizeof(nfs_fwrite_info_t), &wnfs->lock)) {
// // vaddr = ambarella_phys_to_virt(nfs_fwrite_info.addr);
// // bytes_read += nfs_fwrite_info.size*nfs_fwrite_info.count;
// // memcpy(buf, vaddr, nfs_fwrite_info.size*nfs_fwrite_info.count);
// // buf += bytes_read;
// //printk("device_read: x%x x%x\n", vaddr, bytes_read);
// bytes_read = len;
// }
//rpmsg_sendto(g_wnfs.rpdev, &bytes_read, sizeof(int), g_wnfs.rpdev->dst);
//printk("device_read: final bytes_read %d/%d \n", bytes_read, len);
return bytes_read;
}
static int device_release(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
static int wnfs_init(struct wnfs_struct *wnfs)
{
int ret;
spin_lock_init(&wnfs->lock);
ret = kfifo_alloc(&wnfs->fifo, 4096*16, GFP_KERNEL);
if (ret)
return ret;
printk("RPMSG Write to NFS Server [ARM11] now is ready at /dev/%s\n", DEVICE_NAME);
wnfs->fdev.name = DEVICE_NAME;
wnfs->fdev.fops = &fops;
memset(&(wnfs->unfinished), 0, sizeof(nfs_fwrite_info_t));
misc_register(&wnfs->fdev);
return 0;
}
static void rpmsg_wnfs_cb(struct rpmsg_channel *rpdev, void *data,
int len, void *priv, u32 src)
{
struct wnfs_struct *wnfs = priv;
int n;
//printk("rpmsg_wnfs_cb +\n");
n = kfifo_in_locked(&wnfs->fifo, (char *)data, len, &wnfs->lock);
//printk("rpmsg_wnfs_cb -:%d\n", n);
//rpmsg_sendto(rpdev, &len, sizeof(int), rpdev->dst);
}
static int rpmsg_wnfs_probe(struct rpmsg_channel *rpdev)
{
int ret = 0;
struct rpmsg_ns_msg nsm;
struct wnfs_struct *wnfs = &g_wnfs;
nsm.addr = rpdev->dst;
memcpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE);
nsm.flags = 0;
wnfs_init(wnfs);
wnfs->rpdev = rpdev;
rpdev->ept->priv = wnfs;
rpmsg_sendto(rpdev, &nsm, sizeof(nsm), rpdev->dst);
return ret;
}
static void rpmsg_wnfs_remove(struct rpmsg_channel *rpdev)
{
struct wnfs_struct *wnfs = &g_wnfs;
misc_deregister(&wnfs->fdev);
}
static struct rpmsg_device_id rpmsg_wnfs_id_table[] = {
{ .name = "wnfs_arm11", },
{ },
};
MODULE_DEVICE_TABLE(rpmsg, rpmsg_wnfs_id_table);
static struct rpmsg_driver rpmsg_wnfs_driver = {
.drv.name = KBUILD_MODNAME,
.drv.owner = THIS_MODULE,
.id_table = rpmsg_wnfs_id_table,
.probe = rpmsg_wnfs_probe,
.callback = rpmsg_wnfs_cb,
.remove = rpmsg_wnfs_remove,
};
static int __init rpmsg_wnfs_init(void)
{
return register_rpmsg_driver(&rpmsg_wnfs_driver);
}
static void __exit rpmsg_wnfs_fini(void)
{
unregister_rpmsg_driver(&rpmsg_wnfs_driver);
}
module_init(rpmsg_wnfs_init);
module_exit(rpmsg_wnfs_fini);
MODULE_DESCRIPTION("RPMSG Write to NFS Server");