blob: 2def2c91f6310a5aa2d49832361c4e3ad35bec59 [file] [log] [blame]
#include <linux/module.h>
#include <linux/rpmsg.h>
#include <linux/err.h>
#include <linux/remoteproc.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/kfifo.h>
#include <linux/delay.h>
#include <plat/ambcache.h>
typedef struct xnfs_info_s {
char *addr;
int size;
int count;
void *priv;
} xnfs_info_t;
struct xnfs_struct {
int id;
struct kfifo fifo;
spinlock_t lock;
wait_queue_head_t queue;
struct rpmsg_channel *rpdev;
};
static struct xnfs_struct g_wnfs;
static struct xnfs_struct g_rnfs;
ssize_t xnfs_read(char *buf, size_t len)
{
struct xnfs_struct *xnfs = &g_wnfs;
xnfs_info_t info;
int n = 0;
char *va;
struct timespec ts, ts1, ts2;
getnstimeofday(&ts1);
wait_event_interruptible(xnfs->queue,
kfifo_out_locked(&xnfs->fifo, &info,
sizeof(info), &xnfs->lock));
getnstimeofday(&ts2);
if (!info.size) {
rpmsg_send(xnfs->rpdev, &n, sizeof(n));
return 0;
}
va = (void *)ambarella_phys_to_virt((unsigned long)info.addr);
n = info.size * info.count;
ts = timespec_sub(ts2, ts1);
printk(KERN_DEBUG "rpmsg: Linux got 0x%08x Bytes for %lu.%09lu Seconds\n",
info.count, ts.tv_sec, ts.tv_nsec);
ambcache_flush_range((void *)va, n);
memcpy(buf, va, n);
/* ack to release fread of iTRON side */
rpmsg_send(xnfs->rpdev, &n, sizeof(n));
return n;
}
ssize_t xnfs_write(char *buf, size_t len)
{
struct xnfs_struct *xnfs = &g_rnfs;
char *va;
int size;
xnfs_info_t info;
wait_event_interruptible(xnfs->queue,
kfifo_out_locked(&xnfs->fifo, &info,
sizeof(info), &xnfs->lock));
va = (void *)ambarella_phys_to_virt((unsigned long)info.addr);
size = info.size * info.count;
BUG_ON(size < len);
if (len > 0) {
memcpy(va, buf, len);
ambcache_clean_range((void *)va, len);
}
if (len == 0)
printk(KERN_DEBUG "rpmsg: uItron got 0 bytes\n");
rpmsg_send(xnfs->rpdev, &len, sizeof(len));
return len;
}
static void rpmsg_xnfs_cb(struct rpmsg_channel *rpdev, void *data,
int len, void *priv, u32 src)
{
struct xnfs_struct *xnfs = priv;
BUG_ON(len != sizeof(xnfs_info_t));
kfifo_in_locked(&xnfs->fifo, (char *)data, len, &xnfs->lock);
wake_up_interruptible(&xnfs->queue);
}
static int xnfs_init(struct xnfs_struct *xnfs)
{
int ret;
spin_lock_init(&xnfs->lock);
init_waitqueue_head(&xnfs->queue);
ret = kfifo_alloc(&xnfs->fifo, 4096 * 16, GFP_KERNEL);
if (ret)
return ret;
return 0;
}
static struct rpmsg_device_id rpmsg_xnfs_id_table[] = {
{ .name = "rnfs_arm11", },
{ .name = "wnfs_arm11", },
{ },
};
MODULE_DEVICE_TABLE(rpmsg, rpmsg_xnfs_id_table);
static int rpmsg_xnfs_probe(struct rpmsg_channel *rpdev)
{
int ret = 0;
struct rpmsg_ns_msg nsm;
struct xnfs_struct *xnfs = NULL;
nsm.addr = rpdev->dst;
memcpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE);
nsm.flags = 0;
if (!strcmp(rpdev->id.name, rpmsg_xnfs_id_table[0].name)) {
xnfs = &g_rnfs;
xnfs->id = 0;
printk("RPMSG Ready from NFS Server [ARM11] is ready\n");
} else if (!strcmp(rpdev->id.name, rpmsg_xnfs_id_table[1].name)) {
xnfs = &g_wnfs;
xnfs->id = 1;
printk("RPMSG Write to NFS Server [ARM11] is ready\n");
}
xnfs_init(xnfs);
xnfs->rpdev = rpdev;
rpdev->ept->priv = xnfs;
rpmsg_send(rpdev, &nsm, sizeof(nsm));
return ret;
}
static void rpmsg_xnfs_remove(struct rpmsg_channel *rpdev)
{
}
struct rpmsg_driver rpmsg_xnfs_driver = {
.drv.name = KBUILD_MODNAME,
.drv.owner = THIS_MODULE,
.id_table = rpmsg_xnfs_id_table,
.probe = rpmsg_xnfs_probe,
.callback = rpmsg_xnfs_cb,
.remove = rpmsg_xnfs_remove,
};