blob: 59e64d47be8082d35c125c06aff63906d86e0c61 [file] [log] [blame]
/*
* rpmsg audio service 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>
#include <linux/mm.h>
#include <linux/wait.h>
#define DEVICE_NAME "aservice"
enum aio_cmd {
AIO_CMD_SETUP = 0,
AIO_CMD_START = 1,
AIO_CMD_STOP = 2,
AIO_CMD_CLOSE = 3,
AIO_CMD_NONE = 0xFF
};
enum aio_type {
ENCODER = 0,
DECODER,
XCODER,
BYPASS
};
enum aio_dir {
ARM_TO_LINUX = 0,
LINUX_TO_ARM,
BIDRECTION,
};
enum aio_encoder {
AAC_ENC = 0,
AC3_ENC,
MP2_ENC
};
enum aio_decoder {
AAC_DEC = 0,
AC3_DEC,
MP2_DEC
};
//test aservice structure
typedef struct aio_buf_s {
u32 command;
u8 *arm_buf_base; /**< ARM side buffer write pointer >*/
u32 arm_buf_limit; /**< ARM side buffer write pointer >*/
u32 arm_buf_wptr; /**< ARM side buffer write pointer >*/
u32 arm_buf_rptr; /**< ARM side buffer read pointer >*/
u32 arm_buf_vaddr; /** <Linux side buffer virtual address (mmap), for iTron side debug or information > */
u8 *linux_buf_base; /**< ARM side buffer write pointer >*/
u32 linux_buf_limit; /**< ARM side buffer write pointer >*/
u32 linux_buf_wptr; /**< Linux side buffer write pointer >*/
u32 linux_buf_rptr; /**< Linux side buffer read pointer >*/
u32 linux_buf_vaddr; /** <Linux side buffer virtual address (mmap), for iTron side debug or information > */
u32 decoder;
u32 encoder;
u32 aio_type;
u32 aio_direction;
u32 self;
} aio_buf_t;
struct aservice_ret_msg {
s32 rval;
};
struct aservice_struct {
spinlock_t lock;
struct rpmsg_channel *rpdev;
struct miscdevice fdev;
aio_buf_t *aio_buf;
u32 finish_flag;
};
//init waiting queue
static DECLARE_WAIT_QUEUE_HEAD(msgq);
static struct aservice_struct g_aservice;
static int device_open(struct inode *inode, struct file *filp)
{
filp->private_data = &g_aservice;
return 0;
}
static ssize_t device_write(struct file *filp, const char *buf,
size_t count, loff_t * off)
{
struct aservice_struct *aservice = filp->private_data;
printk("Enter devide write\n");
//might deal with different write msgs
aservice->finish_flag = 1;
wake_up_interruptible(&msgq);
return count;
}
static ssize_t device_read(struct file *filp, char *buf,
size_t len, loff_t * offset)
{
struct aservice_struct *aservice = filp->private_data;
aio_buf_t *aio_buf = aservice->aio_buf;
int rval;
printk("Enter device read\n");
if (aio_buf != NULL) {
switch(aio_buf->command) {
case AIO_CMD_SETUP:
case AIO_CMD_START:
case AIO_CMD_STOP:
case AIO_CMD_CLOSE: {
printk("Command! %d\n", aio_buf->command);
rval = copy_to_user(buf, aio_buf, sizeof(aio_buf_t));
if(rval)
{
printk("copy to user failed %d %d\n", rval, sizeof(aio_buf_t));
return -1;
}
aio_buf->command = AIO_CMD_NONE;
break;
}
case AIO_CMD_NONE: //no command
return -1;
break;
}
return 0;
}
else
return -1;
}
static int device_release(struct inode *inode, struct file *file)
{
return 0;
}
static int device_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct aservice_struct *aservice = filp->private_data;
aio_buf_t *aio_buf = aservice->aio_buf;
int rval = 0;
unsigned long size;
size = vma->vm_end - vma->vm_start; //size from userspace
printk("Doing mmap, size = %lu\n", size);
vma->vm_pgoff = (aio_buf->self) >> PAGE_SHIFT;
rval = remap_pfn_range(vma,
vma->vm_start,
vma->vm_pgoff,
size,
vma->vm_page_prot);
if (rval < 0)
printk("We got some error\n");
//mmap this size of memory based on memory address from uItron side
//maybe more memory management if there is only one kernel device
return rval;
}
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.mmap = device_mmap,
.release = device_release
};
static int aservice_init(struct aservice_struct *aservice)
{
spin_lock_init(&aservice->lock);
printk("RPMSG aservice [ARM11] now is ready at /dev/%s\n", DEVICE_NAME);
aservice->fdev.name = DEVICE_NAME;
aservice->fdev.fops = &fops;
aservice->aio_buf = NULL;
aservice->finish_flag = 0;
misc_register(&aservice->fdev);
return 0;
}
static void rpmsg_aservice_cb(struct rpmsg_channel *rpdev, void *data,
int len, void *priv, u32 src)
{
struct aservice_struct *aservice = priv;
struct aservice_ret_msg msg;
aio_buf_t *aio_buf_test;
aio_buf_t *aio_buf = data;
aservice->aio_buf = data;
printk("recieve msg : rpmsg_aservice_cb +\n");
printk("length %d\n", len);
printk("My address is %08X\n", (u32)aio_buf);
printk("command %d\n",aio_buf->command);
printk("buf base %08X\n",(u32)aio_buf->arm_buf_base); /**< ARM side buffer write pointer >*/
printk("buf limit %08X\n",aio_buf->arm_buf_limit); /**< ARM side buffer write pointer >*/
printk("wptr %08X\n",aio_buf->arm_buf_wptr); /**< ARM side buffer write pointer >*/
printk("rptr %08X\n",aio_buf->arm_buf_rptr); /**< ARM side buffer read pointer >*/
printk("buf vaddr %08X\n",aio_buf->arm_buf_vaddr); /** <Linux side buffer virtual address (mmap), for iTron side debug or information > */
printk("buf base %08X\n",(u32)aio_buf->linux_buf_base); /**< ARM side buffer write pointer >*/
printk("buf limit %08X\n",aio_buf->linux_buf_limit); /**< ARM side buffer write pointer >*/
printk("buf wptr %08X\n",aio_buf->linux_buf_wptr); /**< Linux side buffer write pointer >*/
printk("rptr %08X\n",aio_buf->linux_buf_rptr); /**< Linux side buffer read pointer >*/
printk("buf vaddr %08X\n",aio_buf->linux_buf_vaddr); /** <Linux side buffer virtual address (mmap), for iTron side debug or information > */
printk("encode %d\n",aio_buf->encoder);
printk("decode %d\n",aio_buf->decoder);
printk("type %d\n",aio_buf->aio_type);
printk("dir %d\n",aio_buf->aio_direction);
printk("self %08X\n", aio_buf->self);
aio_buf_test = (aio_buf_t *)phys_to_virt(aio_buf->self);
printk("aio buf test to %08X\n", (u32)aio_buf_test);
printk("command %d\n",aio_buf_test->command);
printk("buf base %08X\n",(u32)aio_buf_test->arm_buf_base); /**< ARM side buffer write pointer >*/
printk("buf limit %08X\n",aio_buf_test->arm_buf_limit); /**< ARM side buffer write pointer >*/
printk("wptr %08X\n",aio_buf_test->arm_buf_wptr); /**< ARM side buffer write pointer >*/
printk("rptr %08X\n",aio_buf_test->arm_buf_rptr); /**< ARM side buffer read pointer >*/
printk("buf vaddr %08X\n",aio_buf_test->arm_buf_vaddr); /** <Linux side buffer virtual address (mmap), for iTron side debug or information > */
printk("Let's write something\n");
aio_buf_test->command = 2;
aio_buf_test->arm_buf_base = 0xDEADBEEF;
aio_buf_test->arm_buf_limit = 0x2000;
aio_buf_test->arm_buf_wptr = 0x12345678;
aio_buf_test->arm_buf_rptr = 0x87654321;
aio_buf_test->arm_buf_vaddr = 0x11111111;
printk("command %d\n",aio_buf_test->command);
printk("buf base %08X\n",(u32)aio_buf_test->arm_buf_base); /**< ARM side buffer write pointer >*/
printk("buf limit %08X\n",aio_buf_test->arm_buf_limit); /**< ARM side buffer write pointer >*/
printk("wptr %08X\n",aio_buf_test->arm_buf_wptr); /**< ARM side buffer write pointer >*/
printk("rptr %08X\n",aio_buf_test->arm_buf_rptr); /**< ARM side buffer read pointer >*/
printk("buf vaddr %08X\n",aio_buf_test->arm_buf_vaddr); /** <Linux side buffer virtual address (mmap), for iTron side debug or information > */
switch(aio_buf->command)
{
case AIO_CMD_SETUP:
printk("setup command\n");
break;
case AIO_CMD_START:
printk("start command\n");
break;
case AIO_CMD_STOP:
printk("start command\n");
break;
case AIO_CMD_CLOSE:
printk("close command\n");
default:
printk("Bad command\n");
break;
}
msg.rval = -1;
printk("msg val %d\n", msg.rval);
wait_event_interruptible(msgq, aservice->finish_flag != 0);
if (aservice->finish_flag)
aservice->finish_flag = 0;
printk("waiting queue done, send back to uItron\n");
//return a msg done, will using wait queue for application handshaking
rpmsg_sendto(rpdev, &msg, sizeof(msg), rpdev->dst);
}
static int rpmsg_aservice_probe(struct rpmsg_channel *rpdev)
{
int ret = 0;
struct rpmsg_ns_msg nsm;
struct aservice_struct *aservice = &g_aservice;
nsm.addr = rpdev->dst;
memcpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE);
nsm.flags = 0;
aservice_init(aservice);
aservice->rpdev = rpdev;
rpdev->ept->priv = aservice;
rpmsg_sendto(rpdev, &nsm, sizeof(nsm), rpdev->dst);
return ret;
}
static void rpmsg_aservice_remove(struct rpmsg_channel *rpdev)
{
struct aservice_struct *aservice = &g_aservice;
misc_deregister(&aservice->fdev);
}
static struct rpmsg_device_id rpmsg_aservice_id_table[] = {
{ .name = "aservice_arm11", },
{ },
};
MODULE_DEVICE_TABLE(rpmsg, rpmsg_aservice_id_table);
static struct rpmsg_driver rpmsg_aservice_driver = {
.drv.name = KBUILD_MODNAME,
.drv.owner = THIS_MODULE,
.id_table = rpmsg_aservice_id_table,
.probe = rpmsg_aservice_probe,
.callback = rpmsg_aservice_cb,
.remove = rpmsg_aservice_remove,
};
static int __init rpmsg_aservice_init(void)
{
return register_rpmsg_driver(&rpmsg_aservice_driver);
}
static void __exit rpmsg_aservice_fini(void)
{
unregister_rpmsg_driver(&rpmsg_aservice_driver);
}
module_init(rpmsg_aservice_init);
module_exit(rpmsg_aservice_fini);
MODULE_DESCRIPTION("RPMSG audio service");