blob: b06074d24620b6abf099081b3d3cb65c3681c31b [file] [log] [blame]
/*
* rpmsg read from 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 "rnfs"
typedef int redev_nfs_FILE;
typedef struct nfs_fread_info_s {
int *addr;
int size;
int count;
redev_nfs_FILE *file;
}
nfs_fread_info_t;
struct rnfs_struct {
struct kfifo fifo;
spinlock_t lock;
struct rpmsg_channel *rpdev;
struct miscdevice fdev;
nfs_fread_info_t unfinished;
};
static struct rnfs_struct g_rnfs;
static int device_open(struct inode *inode, struct file *filp)
{
filp->private_data = &g_rnfs;
return 0;
}
static ssize_t device_write(struct file *filp, const char *buf,
size_t count, loff_t *off)
{
struct rnfs_struct *rnfs = filp->private_data;
//struct rpmsg_channel *rpdev = rnfs->rpdev;
unsigned int ret;
unsigned int avail;
unsigned int unfinished = count;
unsigned int cpsize;
unsigned int cpsize_total;
char *rp = buf;
//printk("device_write: rp 0x%x unfinished %x\n", rp, unfinished);
cpsize_total = 0;
while(unfinished > 0) {
avail = kfifo_avail(&rnfs->fifo);
if(avail == 0) {
//printk("rpmsg_rnfs_cb: msleep(1000);");
msleep(5);
cpsize = 0;
} else if(avail > unfinished){
cpsize = unfinished;
} else if(avail <= unfinished) {
cpsize = avail;
}
if(cpsize > 0) {
ret = kfifo_in_locked(&rnfs->fifo, (char *)rp, cpsize, &rnfs->lock);
rp += cpsize;
unfinished -= cpsize;
cpsize_total += cpsize;
}
//printk("device_write: avail %x rp 0x%x unfinished %x\n", avail, rp, unfinished);
}
if(cpsize_total != count)
printk("rnfs device_write: cpsize_total != count\n");
return cpsize_total;
}
int rnfs_test_counter = 0;
static ssize_t device_read(struct file *filp, char *buf,
size_t len, loff_t *offset)
{
struct rnfs_struct *rnfs = filp->private_data;
int bytes_read = 0;
// char c;
char *vaddr;
nfs_fread_info_t nfs_fread_info;
int copy_size;
int unfinished_size;
int ret;
int len_remain = len;
//printk(" ====================== device_read %d ======================\n", rnfs_test_counter);
//rnfs_test_counter ++;
if(rnfs_test_counter > 2000) {
printk("device_read: force 0\n");
rnfs_test_counter = 0;
return 0;
}
// while (kfifo_out_locked(&rnfs->fifo, &c, sizeof(c), &rnfs->lock)) {
//
// *buf++ = c;
// bytes_read++;
// }
// blocking = filp->??
while(len_remain > 0) {
switch (rnfs->unfinished.count) {
case 0:
//reload
// while(1) {
ret = kfifo_out_locked(&rnfs->fifo, &nfs_fread_info, sizeof(nfs_fread_info_t), &rnfs->lock);
//printk("reload: info %d/%d\n", ret, sizeof(nfs_fread_info_t));
// //if ret==not ready, sleep
// if(ret < sizeof(nfs_fread_info_t))
// if(blocking)
// sleep();
// else return -EAGAIN;
// else
// break;
// }
if(ret < sizeof(nfs_fread_info_t)) {
msleep(5);
//printk("reload: not ready %d\n", ret);
break;
} else {
if(nfs_fread_info.size == 0xdeadbeef) {
printk("nfs_fread_info.size == 0xdeadbeef");
rnfs_test_counter = 2000000;
rpmsg_sendto(rnfs->rpdev, &bytes_read, sizeof(int), rnfs->rpdev->dst); //ack to release fread of iTRON side
return 0;
}
memcpy(&(rnfs->unfinished), &nfs_fread_info, sizeof(nfs_fread_info_t));
//printk("reload: x%x %d %d\n", rnfs->unfinished.addr, rnfs->unfinished.size, rnfs->unfinished.count);
}
default:
//fill-up buf
unfinished_size = rnfs->unfinished.size * rnfs->unfinished.count;
copy_size = (len_remain > unfinished_size) ? unfinished_size : len_remain;
if(copy_size>0) {
vaddr = (void *)ambarella_phys_to_virt((unsigned long)rnfs->unfinished.addr);
memcpy(buf, vaddr, copy_size);
bytes_read += copy_size;
buf += copy_size;
unfinished_size -= copy_size;
if(unfinished_size > 0) {
rnfs->unfinished.count = unfinished_size / rnfs->unfinished.size;
rnfs->unfinished.addr = (int *)((unsigned int)rnfs->unfinished.addr + copy_size);
} else {
memset(&(rnfs->unfinished), 0, sizeof(nfs_fread_info_t));
rpmsg_sendto(rnfs->rpdev, &bytes_read, sizeof(int), rnfs->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(&rnfs->fifo, &nfs_fread_info, sizeof(nfs_fread_info_t), &rnfs->lock)) {
// // vaddr = ambarella_phys_to_virt(nfs_fread_info.addr);
// // bytes_read += nfs_fread_info.size*nfs_fread_info.count;
// // memcpy(buf, vaddr, nfs_fread_info.size*nfs_fread_info.count);
// // buf += bytes_read;
// //printk("device_read: x%x x%x\n", vaddr, bytes_read);
// bytes_read = len;
// }
//rpmsg_sendto(g_rnfs.rpdev, &bytes_read, sizeof(int), g_rnfs.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 rnfs_init(struct rnfs_struct *rnfs)
{
int ret;
spin_lock_init(&rnfs->lock);
ret = kfifo_alloc(&rnfs->fifo, 1024*1024*2, GFP_KERNEL);
if (ret)
return ret;
printk("RPMSG Read from NFS Server [ARM11] now is ready at /dev/%s\n", DEVICE_NAME);
rnfs->fdev.name = DEVICE_NAME;
rnfs->fdev.fops = &fops;
rnfs->fdev.minor = MISC_DYNAMIC_MINOR;
memset(&(rnfs->unfinished), 0, sizeof(nfs_fread_info_t));
misc_register(&rnfs->fdev);
return 0;
}
static void rpmsg_rnfs_cb(struct rpmsg_channel *rpdev, void *data,
int len, void *priv, u32 src)
{
struct rnfs_struct *rnfs = priv;
unsigned int ret;
unsigned int avail;
unsigned int unfinished = len;
unsigned int cpsize;
unsigned int cpsize_total;
char *wp;
nfs_fread_info_t *info = data;
#define EOF_TIMEOUT (20)
unsigned int timeout = EOF_TIMEOUT;
cpsize_total = 0;
unfinished = info->size*info->count;
printk("rpmsg_rnfs_cb: p 0x%x v 0x%x %x %x\n", info->addr, wp, info->size, info->count);
wp = (void *)ambarella_phys_to_virt((unsigned long)info->addr);
printk("rpmsg_rnfs_cb: p 0x%x v 0x%x %x %x\n", info->addr, wp, info->size, info->count);
while(unfinished > 0) {
avail = kfifo_len(&rnfs->fifo);
if(avail == 0) {
//printk("rpmsg_rnfs_cb: msleep(1000);");
msleep(5);
cpsize = 0;
timeout--;
if(timeout == 0) {
printk("rpmsg_rnfs_cb: could be eof\n");
break;
}
} else if(avail > unfinished){
cpsize = unfinished;
} else if(avail <= unfinished) {
cpsize = avail;
}
if(cpsize > 0) {
ret = kfifo_out_locked(&rnfs->fifo, wp, cpsize, &rnfs->lock);
wp += cpsize;
unfinished -= cpsize;
cpsize_total += cpsize;
}
printk("rpmsg_rnfs_cb: avail %x wp %x, unfinished %x\n", avail, wp, unfinished);
}
if(cpsize_total != info->size*info->count)
printk("rnfs rpmsg_rnfs_cb: cpsize_total != count\n");
//ack to release fread of iTRON side
rpmsg_sendto(rnfs->rpdev, &cpsize_total, sizeof(unsigned int), rnfs->rpdev->dst);
printk("rnfs rpmsg_rnfs_cb: ack iTRON\n");
}
static int rpmsg_rnfs_probe(struct rpmsg_channel *rpdev)
{
int ret = 0;
struct rpmsg_ns_msg nsm;
struct rnfs_struct *rnfs = &g_rnfs;
nsm.addr = rpdev->dst;
memcpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE);
nsm.flags = 0;
rnfs_init(rnfs);
rnfs->rpdev = rpdev;
rpdev->ept->priv = rnfs;
rpmsg_sendto(rpdev, &nsm, sizeof(nsm), rpdev->dst);
return ret;
}
static void rpmsg_rnfs_remove(struct rpmsg_channel *rpdev)
{
struct rnfs_struct *rnfs = &g_rnfs;
misc_deregister(&rnfs->fdev);
}
static struct rpmsg_device_id rpmsg_rnfs_id_table[] = {
{ .name = "rnfs_arm11", },
{ },
};
MODULE_DEVICE_TABLE(rpmsg, rpmsg_rnfs_id_table);
static struct rpmsg_driver rpmsg_rnfs_driver = {
.drv.name = KBUILD_MODNAME,
.drv.owner = THIS_MODULE,
.id_table = rpmsg_rnfs_id_table,
.probe = rpmsg_rnfs_probe,
.callback = rpmsg_rnfs_cb,
.remove = rpmsg_rnfs_remove,
};
static int __init rpmsg_rnfs_init(void)
{
return register_rpmsg_driver(&rpmsg_rnfs_driver);
}
static void __exit rpmsg_rnfs_fini(void)
{
unregister_rpmsg_driver(&rpmsg_rnfs_driver);
}
module_init(rpmsg_rnfs_init);
module_exit(rpmsg_rnfs_fini);
MODULE_DESCRIPTION("RPMSG Read from NFS Server");