msm: adsprpc: RPC driver between apps and adsp

The driver implements an IPC (Inter-Processor) communication
mechanism that allows for clients to make remote method
invocations across processor boundary.

Change-Id: I95710df16e9258ad37b1f3d1e32f36003760b970
Acked-by: Sathish Ambley <sambley@qualcomm.com>
Signed-off-by: Mitchel Humpherys <mitchelh@codeaurora.org>
This commit is contained in:
Mitchel Humpherys 2012-08-29 16:20:15 -07:00 committed by Stephen Boyd
parent 683714f4e7
commit cb14e3d32d
5 changed files with 1158 additions and 0 deletions

View file

@ -0,0 +1,198 @@
Introduction
============
The MSM ADSPRPC driver implements an IPC (Inter-Processor Communication)
mechanism that allows for clients to transparently make remote method
invocations across processor boundaries.
The below diagram depicts invocation of a single method where the client
and objects reside on different processors. An object could expose
multiple methods which can be grouped together and referred to as an
interface.
: ,--------, ,------, ,-----------, ,------, ,--------,
: | | method | | | | | | method | |
: | Client |------->| Stub |->| Transport |->| Skel |------->| Object |
: | | | | | | | | | |
: `--------` `------` `-----------` `------` `--------`
Client: Linux user mode process that initiates the remote invocation
Stub: Auto generated code linked in with the user mode process that
takes care of marshaling parameters
Transport: Involved in carrying an invocation from a client to an
object. This involves two portions: 1) MSM ADSPRPC Linux
kernel driver that receives the remote invocation, queues
them up and then waits for the response after signaling the
remote side. 2) Service running on the remote side that
dequeues the messages from the queue and dispatches them for
processing.
Skel: Auto generated code that takes care of un-marshaling
parameters
Object: Method implementation
Hardware description
====================
The driver interfaces with the components in the DSP subsystem and does
not drive or manage any hardware resources.
Software description
====================
The MSM ADSPRPC driver uses SMD (Shared Memory Driver) to send and
receive messages with the remote processor. The SMD channel used for
communication is opened during initialization of the driver and is
closed when the driver module is unloaded. The driver does not expose
HLOS memory to the remote processor but rather communication of
invocation parameters happen over ION allocated buffers.
The driver receives remote call invocations via an ioctl call. When a
remote call invocation is received, the driver does the following:
- Retrieves the invocation parameters
- Copies input buffers in HLOS memory to ION allocated buffers
- Allocates ION buffers for output buffers in HLOS memory as required
- Scatter-gathers list of pages for ION allocated input and output
buffers
- Coalesces information about the contiguous page buffers
- Builds up a message with the received information
- Sends the message to a remote processor through an SMD channel
- Waits for a response from the remote processor through the SMD channel
- Reads the message available from the shared memory SMD channel
- Copies back from ION buffers to HLOS memory for output buffers
- Returns the response of the remote invocation
Design
======
The design goals of this transport mechanism are:
- Fast and efficient ways to transfer huge buffers across
inter-processor boundaries
- Zero copy of ION allocated buffers passed during invocations
To achieve the zero copy approach of ION allocated user space buffers,
the driver scatter-gathers the list of pages of the buffers being passed
in. This information is then sent over to the remote processor for it
to map into its address space.
The invocation requests sent over the SMD channel carry context
information as to whom the request is originating from. The responses
received over the SMD channel have context information in the message
which is then used to wake the thread waiting for a response.
If the remote processor goes down and gets restarted, the SMD channel
is re-initialized when the remote processor comes back up. An
error code would be returned to the client for all invocations that
happen before the SMD channel could get completely re-initialized.
Power Management
================
None
SMP/multi-core
==============
The driver uses semaphores to wake up clients waiting for a remote
invocation response.
Security
========
Use of the zero copy approach results in a page-size granularity of
all buffers being passed to the remote processor. The objects that will
be manipulating these buffers on the remote processor will be signed
and trusted entities, thereby alleviating any fear of intentional
scribbling of these buffers.
Performance
===========
In order to minimize latencies across remote invocations:
- messages exchanged between the remote processors are kept short
- zero copy approach for ION allocated user space buffers
Interface
=========
The driver exposes a user space interface through /dev/adsprpc-smd and
the user space clients send commands to the driver by using the
following ioctl command:
- FASTRPC_IOCTL_INVOKE: Parameters passed in includes the buffers and
data related to remote invocation.
/*
* Information about the input/output buffer or an handle to the
* object being passed in the remote invocation
*
* @pv: Pointer to input/output buffer
* @len: Length of the input/output buffer
* @handle: Handle to the remote object
*/
typedef union {
struct remote_buf {
void *pv;
int len;
} buf;
unsigned int handle;
} remote_arg;
/*
* Invocation parameters passed via ioctl call by the client
*
* @handle: Handle to the object on which the method is to be
* invoked
* @sc: Scalars detailing the parameters being passed in
* bits 0-3: Number of output handles
* bits 4-7: Number of input handles
* bits 8-15: Number of output buffers
* bits 16-23: Number of input buffers
* bits 24-28: Method to be invoked
* bits 29-31: Method attributes
* @pra: Remote arguments to be passed for method invocation
*/
struct fastrpc_ioctl_invoke {
unsigned int handle;
unsigned int sc;
remote_arg *pra;
};
Driver parameters
=================
None
Config options
==============
None
Dependencies
============
The ADSPRPC driver requires that the ADSP RPC SMD channel be created and
the SMD subsystem be initialized. During initialization, the driver
opens an existing SMD edge channel between ADSP and Apps processor. On
success, the driver waits for the "channel opened" event from SMD,
acknowledging the channel availability from the remote SMD driver for
communication to begin.
User space utilities
====================
None
Other
=====
None
Known issues
============
None
To do
=====
None

View file

@ -26,3 +26,4 @@ obj-$(CONFIG_MSM_QDSP6V2_CODECS) += rtac_v2.o q6audio_v2.o q6audio_v2_aio.o
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_evrc.o audio_qcelp.o amrwb_in.o
obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o
obj-$(CONFIG_MSM_ULTRASOUND) += ultrasound/
obj-m += adsprpc.o

View file

@ -0,0 +1,706 @@
/*
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "adsprpc.h"
struct smq_invoke_ctx {
struct completion work;
int retval;
atomic_t free;
};
struct smq_context_list {
struct smq_invoke_ctx *ls;
int size;
int last;
};
struct fastrpc_apps {
smd_channel_t *chan;
struct smq_context_list clst;
struct completion work;
struct ion_client *iclient;
struct cdev cdev;
dev_t dev_no;
spinlock_t wrlock;
spinlock_t hlock;
struct hlist_head htbl[RPC_HASH_SZ];
};
struct fastrpc_buf {
struct ion_handle *handle;
void *virt;
ion_phys_addr_t phys;
int size;
int used;
};
struct fastrpc_device {
uint32_t tgid;
struct hlist_node hn;
struct fastrpc_buf buf;
};
static struct fastrpc_apps gfa;
static void free_mem(struct fastrpc_buf *buf)
{
struct fastrpc_apps *me = &gfa;
if (buf->handle) {
if (buf->virt) {
ion_unmap_kernel(me->iclient, buf->handle);
buf->virt = 0;
}
ion_free(me->iclient, buf->handle);
buf->handle = 0;
}
}
static int alloc_mem(struct fastrpc_buf *buf)
{
struct ion_client *clnt = gfa.iclient;
int err = 0;
buf->handle = ion_alloc(clnt, buf->size, SZ_4K,
ION_HEAP(ION_AUDIO_HEAP_ID));
VERIFY(0 == IS_ERR_OR_NULL(buf->handle));
buf->virt = 0;
VERIFY(0 != (buf->virt = ion_map_kernel(clnt, buf->handle,
ION_SET_CACHE(CACHED))));
VERIFY(0 == ion_phys(clnt, buf->handle, &buf->phys, &buf->size));
bail:
if (err && !IS_ERR_OR_NULL(buf->handle))
free_mem(buf);
return err;
}
static int context_list_ctor(struct smq_context_list *me, int size)
{
int err = 0;
VERIFY(0 != (me->ls = kzalloc(size, GFP_KERNEL)));
me->size = size / sizeof(*me->ls);
me->last = 0;
bail:
return err;
}
static void context_list_dtor(struct smq_context_list *me)
{
kfree(me->ls);
me->ls = 0;
}
static void context_list_alloc_ctx(struct smq_context_list *me,
struct smq_invoke_ctx **po)
{
int ii = me->last;
struct smq_invoke_ctx *ctx;
for (;;) {
ii = ii % me->size;
ctx = &me->ls[ii];
if (atomic_read(&ctx->free) == 0)
if (0 == atomic_cmpxchg(&ctx->free, 0, 1))
break;
ii++;
}
me->last = ii;
ctx->retval = -1;
init_completion(&ctx->work);
*po = ctx;
}
static void context_free(struct smq_invoke_ctx *me)
{
if (me)
atomic_set(&me->free, 0);
}
static void context_notify_user(struct smq_invoke_ctx *me, int retval)
{
me->retval = retval;
complete(&me->work);
}
static void context_notify_all_users(struct smq_context_list *me)
{
int ii;
if (!me->ls)
return;
for (ii = 0; ii < me->size; ++ii) {
if (atomic_read(&me->ls[ii].free) != 0)
complete(&me->ls[ii].work);
}
}
static int get_page_list(uint32_t kernel, uint32_t sc, remote_arg_t *pra,
struct fastrpc_buf *ibuf, struct fastrpc_buf *obuf)
{
struct smq_phy_page *pgstart, *pages;
struct smq_invoke_buf *list;
int ii, rlen, err = 0;
int inbufs = REMOTE_SCALARS_INBUFS(sc);
int outbufs = REMOTE_SCALARS_OUTBUFS(sc);
VERIFY(0 != try_module_get(THIS_MODULE));
LOCK_MMAP(kernel);
*obuf = *ibuf;
retry:
list = smq_invoke_buf_start((remote_arg_t *)obuf->virt, sc);
pgstart = smq_phy_page_start(sc, list);
pages = pgstart + 1;
rlen = obuf->size - ((uint32_t)pages - (uint32_t)obuf->virt);
if (rlen < 0) {
rlen = ((uint32_t)pages - (uint32_t)obuf->virt) - obuf->size;
obuf->size += buf_page_size(rlen);
obuf->handle = 0;
VERIFY(0 == alloc_mem(obuf));
goto retry;
}
pgstart->addr = obuf->phys;
pgstart->size = obuf->size;
for (ii = 0; ii < inbufs + outbufs; ++ii) {
void *buf;
int len, num;
len = pra[ii].buf.len;
if (!len)
continue;
buf = pra[ii].buf.pv;
num = buf_num_pages(buf, len);
if (!kernel)
list[ii].num = buf_get_pages(buf, len, num,
ii >= inbufs, pages, rlen / sizeof(*pages));
else
list[ii].num = 0;
VERIFY(list[ii].num >= 0);
if (list[ii].num) {
list[ii].pgidx = pages - pgstart;
pages = pages + list[ii].num;
} else if (rlen > sizeof(*pages)) {
list[ii].pgidx = pages - pgstart;
pages = pages + 1;
} else {
if (obuf->handle != ibuf->handle)
free_mem(obuf);
obuf->size += buf_page_size(sizeof(*pages));
obuf->handle = 0;
VERIFY(0 == alloc_mem(obuf));
goto retry;
}
rlen = obuf->size - ((uint32_t) pages - (uint32_t) obuf->virt);
}
obuf->used = obuf->size - rlen;
bail:
if (err && (obuf->handle != ibuf->handle))
free_mem(obuf);
UNLOCK_MMAP(kernel);
module_put(THIS_MODULE);
return err;
}
static int get_args(uint32_t kernel, uint32_t sc, remote_arg_t *pra,
remote_arg_t *rpra, remote_arg_t *upra,
struct fastrpc_buf *ibuf, struct fastrpc_buf **abufs,
int *nbufs)
{
struct smq_invoke_buf *list;
struct fastrpc_buf *pbuf = ibuf, *obufs = 0;
struct smq_phy_page *pages;
void *args;
int ii, rlen, size, used, inh, bufs = 0, err = 0;
int inbufs = REMOTE_SCALARS_INBUFS(sc);
int outbufs = REMOTE_SCALARS_OUTBUFS(sc);
list = smq_invoke_buf_start(rpra, sc);
pages = smq_phy_page_start(sc, list);
used = ALIGN_8(pbuf->used);
args = (void *)((char *)pbuf->virt + used);
rlen = pbuf->size - used;
for (ii = 0; ii < inbufs + outbufs; ++ii) {
int num;
rpra[ii].buf.len = pra[ii].buf.len;
if (list[ii].num) {
rpra[ii].buf.pv = pra[ii].buf.pv;
continue;
}
if (rlen < pra[ii].buf.len) {
struct fastrpc_buf *b;
pbuf->used = pbuf->size - rlen;
VERIFY(0 != (b = krealloc(obufs,
(bufs + 1) * sizeof(*obufs), GFP_KERNEL)));
obufs = b;
pbuf = obufs + bufs;
pbuf->size = buf_num_pages(0, pra[ii].buf.len) *
PAGE_SIZE;
VERIFY(0 == alloc_mem(pbuf));
bufs++;
args = pbuf->virt;
rlen = pbuf->size;
}
num = buf_num_pages(args, pra[ii].buf.len);
if (pbuf == ibuf) {
list[ii].num = num;
list[ii].pgidx = 0;
} else {
list[ii].num = 1;
pages[list[ii].pgidx].addr =
buf_page_start((void *)(pbuf->phys +
(pbuf->size - rlen)));
pages[list[ii].pgidx].size =
buf_page_size(pra[ii].buf.len);
}
if (ii < inbufs) {
if (!kernel)
VERIFY(0 == copy_from_user(args, pra[ii].buf.pv,
pra[ii].buf.len));
else
memmove(args, pra[ii].buf.pv, pra[ii].buf.len);
}
rpra[ii].buf.pv = args;
args = (void *)((char *)args + ALIGN_8(pra[ii].buf.len));
rlen -= ALIGN_8(pra[ii].buf.len);
}
for (ii = 0; ii < inbufs; ++ii) {
if (rpra[ii].buf.len)
dmac_flush_range(rpra[ii].buf.pv,
(char *)rpra[ii].buf.pv + rpra[ii].buf.len);
}
pbuf->used = pbuf->size - rlen;
size = sizeof(*rpra) * REMOTE_SCALARS_INHANDLES(sc);
if (size) {
inh = inbufs + outbufs;
if (!kernel)
VERIFY(0 == copy_from_user(&rpra[inh], &upra[inh],
size));
else
memmove(&rpra[inh], &upra[inh], size);
}
dmac_flush_range(rpra, (char *)rpra + used);
bail:
*abufs = obufs;
*nbufs = bufs;
return err;
}
static int put_args(uint32_t kernel, uint32_t sc, remote_arg_t *pra,
remote_arg_t *rpra, remote_arg_t *upra)
{
int ii, inbufs, outbufs, outh, size;
int err = 0;
inbufs = REMOTE_SCALARS_INBUFS(sc);
outbufs = REMOTE_SCALARS_OUTBUFS(sc);
for (ii = inbufs; ii < inbufs + outbufs; ++ii) {
if (rpra[ii].buf.pv != pra[ii].buf.pv)
VERIFY(0 == copy_to_user(pra[ii].buf.pv,
rpra[ii].buf.pv, rpra[ii].buf.len));
}
size = sizeof(*rpra) * REMOTE_SCALARS_OUTHANDLES(sc);
if (size) {
outh = inbufs + outbufs + REMOTE_SCALARS_INHANDLES(sc);
if (!kernel)
VERIFY(0 == copy_to_user(&upra[outh], &rpra[outh],
size));
else
memmove(&upra[outh], &rpra[outh], size);
}
bail:
return err;
}
static void inv_args(uint32_t sc, remote_arg_t *rpra, int used)
{
int ii, inbufs, outbufs;
int inv = 0;
inbufs = REMOTE_SCALARS_INBUFS(sc);
outbufs = REMOTE_SCALARS_OUTBUFS(sc);
for (ii = inbufs; ii < inbufs + outbufs; ++ii) {
if (buf_page_start(rpra) == buf_page_start(rpra[ii].buf.pv))
inv = 1;
else
dmac_inv_range(rpra[ii].buf.pv,
(char *)rpra[ii].buf.pv + rpra[ii].buf.len);
}
if (inv || REMOTE_SCALARS_OUTHANDLES(sc))
dmac_inv_range(rpra, (char *)rpra + used);
}
static int fastrpc_invoke_send(struct fastrpc_apps *me, remote_handle_t handle,
uint32_t sc, struct smq_invoke_ctx *ctx,
struct fastrpc_buf *buf)
{
struct smq_msg msg;
int err = 0, len;
msg.pid = current->tgid;
msg.tid = current->pid;
msg.invoke.header.ctx = ctx;
msg.invoke.header.handle = handle;
msg.invoke.header.sc = sc;
msg.invoke.page.addr = buf->phys;
msg.invoke.page.size = buf_page_size(buf->used);
spin_lock(&me->wrlock);
len = smd_write(me->chan, &msg, sizeof(msg));
spin_unlock(&me->wrlock);
VERIFY(len == sizeof(msg));
bail:
return err;
}
static void fastrpc_deinit(void)
{
struct fastrpc_apps *me = &gfa;
if (me->chan)
(void)smd_close(me->chan);
context_list_dtor(&me->clst);
ion_client_destroy(me->iclient);
me->iclient = 0;
me->chan = 0;
}
static void fastrpc_read_handler(void)
{
struct fastrpc_apps *me = &gfa;
struct smq_invoke_rsp rsp;
int err = 0;
do {
VERIFY(sizeof(rsp) ==
smd_read_from_cb(me->chan, &rsp, sizeof(rsp)));
context_notify_user(rsp.ctx, rsp.retval);
} while (!err);
bail:
return;
}
static void smd_event_handler(void *priv, unsigned event)
{
struct fastrpc_apps *me = (struct fastrpc_apps *)priv;
switch (event) {
case SMD_EVENT_OPEN:
complete(&(me->work));
break;
case SMD_EVENT_CLOSE:
context_notify_all_users(&me->clst);
break;
case SMD_EVENT_DATA:
fastrpc_read_handler();
break;
}
}
static int fastrpc_init(void)
{
int err = 0;
struct fastrpc_apps *me = &gfa;
if (me->chan == 0) {
int ii;
spin_lock_init(&me->hlock);
spin_lock_init(&me->wrlock);
init_completion(&me->work);
for (ii = 0; ii < RPC_HASH_SZ; ++ii)
INIT_HLIST_HEAD(&me->htbl[ii]);
VERIFY(0 == context_list_ctor(&me->clst, SZ_4K));
me->iclient = msm_ion_client_create(ION_HEAP_CARVEOUT_MASK,
DEVICE_NAME);
VERIFY(0 == IS_ERR_OR_NULL(me->iclient));
VERIFY(0 == smd_named_open_on_edge(FASTRPC_SMD_GUID,
SMD_APPS_QDSP, &me->chan,
me, smd_event_handler));
VERIFY(0 != wait_for_completion_timeout(&me->work,
RPC_TIMEOUT));
}
bail:
if (err)
fastrpc_deinit();
return err;
}
static void free_dev(struct fastrpc_device *dev)
{
if (dev) {
module_put(THIS_MODULE);
free_mem(&dev->buf);
kfree(dev);
}
}
static int alloc_dev(struct fastrpc_device **dev)
{
int err = 0;
struct fastrpc_device *fd = 0;
VERIFY(0 != try_module_get(THIS_MODULE));
VERIFY(0 != (fd = kzalloc(sizeof(*fd), GFP_KERNEL)));
fd->buf.size = PAGE_SIZE;
VERIFY(0 == alloc_mem(&fd->buf));
fd->tgid = current->tgid;
INIT_HLIST_NODE(&fd->hn);
*dev = fd;
bail:
if (err)
free_dev(fd);
return err;
}
static int get_dev(struct fastrpc_apps *me, struct fastrpc_device **rdev)
{
struct hlist_head *head;
struct fastrpc_device *dev = 0;
struct hlist_node *n;
uint32_t h = hash_32(current->tgid, RPC_HASH_BITS);
int err = 0;
spin_lock(&me->hlock);
head = &me->htbl[h];
hlist_for_each_entry(dev, n, head, hn) {
if (dev->tgid == current->tgid) {
hlist_del(&dev->hn);
break;
}
}
spin_unlock(&me->hlock);
VERIFY(dev != 0);
*rdev = dev;
bail:
if (err) {
free_dev(dev);
err = alloc_dev(rdev);
}
return err;
}
static void add_dev(struct fastrpc_apps *me, struct fastrpc_device *dev)
{
struct hlist_head *head;
uint32_t h = hash_32(current->tgid, RPC_HASH_BITS);
spin_lock(&me->hlock);
head = &me->htbl[h];
hlist_add_head(&dev->hn, head);
spin_unlock(&me->hlock);
return;
}
static int fastrpc_release_current_dsp_process(void);
static int fastrpc_internal_invoke(struct fastrpc_apps *me, uint32_t kernel,
struct fastrpc_ioctl_invoke *invoke, remote_arg_t *pra)
{
remote_arg_t *rpra = 0;
struct fastrpc_device *dev = 0;
struct smq_invoke_ctx *ctx = 0;
struct fastrpc_buf obuf, *abufs = 0, *b;
int interrupted = 0;
uint32_t sc;
int ii, nbufs = 0, err = 0;
sc = invoke->sc;
obuf.handle = 0;
if (REMOTE_SCALARS_LENGTH(sc)) {
VERIFY(0 == get_dev(me, &dev));
VERIFY(0 == get_page_list(kernel, sc, pra, &dev->buf, &obuf));
rpra = (remote_arg_t *)obuf.virt;
VERIFY(0 == get_args(kernel, sc, pra, rpra, invoke->pra, &obuf,
&abufs, &nbufs));
}
context_list_alloc_ctx(&me->clst, &ctx);
VERIFY(0 == fastrpc_invoke_send(me, invoke->handle, sc, ctx, &obuf));
inv_args(sc, rpra, obuf.used);
VERIFY(0 == (interrupted =
wait_for_completion_interruptible(&ctx->work)));
VERIFY(0 == (err = ctx->retval));
VERIFY(0 == put_args(kernel, sc, pra, rpra, invoke->pra));
bail:
if (interrupted) {
init_completion(&ctx->work);
if (!kernel)
(void)fastrpc_release_current_dsp_process();
wait_for_completion(&ctx->work);
}
context_free(ctx);
for (ii = 0, b = abufs; ii < nbufs; ++ii, ++b)
free_mem(b);
kfree(abufs);
if (dev) {
add_dev(me, dev);
if (obuf.handle != dev->buf.handle)
free_mem(&obuf);
}
return err;
}
static int fastrpc_create_current_dsp_process(void)
{
int err = 0;
struct fastrpc_ioctl_invoke ioctl;
struct fastrpc_apps *me = &gfa;
remote_arg_t ra[1];
int tgid = 0;
tgid = current->tgid;
ra[0].buf.pv = &tgid;
ra[0].buf.len = sizeof(tgid);
ioctl.handle = 1;
ioctl.sc = REMOTE_SCALARS_MAKE(0, 1, 0);
ioctl.pra = ra;
VERIFY(0 == fastrpc_internal_invoke(me, 1, &ioctl, ra));
bail:
return err;
}
static int fastrpc_release_current_dsp_process(void)
{
int err = 0;
struct fastrpc_apps *me = &gfa;
struct fastrpc_ioctl_invoke ioctl;
remote_arg_t ra[1];
int tgid = 0;
tgid = current->tgid;
ra[0].buf.pv = &tgid;
ra[0].buf.len = sizeof(tgid);
ioctl.handle = 1;
ioctl.sc = REMOTE_SCALARS_MAKE(1, 1, 0);
ioctl.pra = ra;
VERIFY(0 == fastrpc_internal_invoke(me, 1, &ioctl, ra));
bail:
return err;
}
static void cleanup_current_dev(void)
{
struct fastrpc_apps *me = &gfa;
uint32_t h = hash_32(current->tgid, RPC_HASH_BITS);
struct hlist_head *head;
struct hlist_node *pos;
struct fastrpc_device *dev;
rnext:
dev = 0;
spin_lock(&me->hlock);
head = &me->htbl[h];
hlist_for_each_entry(dev, pos, head, hn) {
if (dev->tgid == current->tgid) {
hlist_del(&dev->hn);
break;
}
}
spin_unlock(&me->hlock);
if (dev) {
free_dev(dev);
goto rnext;
}
return;
}
static int fastrpc_device_release(struct inode *inode, struct file *file)
{
(void)fastrpc_release_current_dsp_process();
cleanup_current_dev();
return 0;
}
static int fastrpc_device_open(struct inode *inode, struct file *filp)
{
int err = 0;
if (0 != try_module_get(THIS_MODULE)) {
/* This call will cause a dev to be created
* which will addref this module
*/
VERIFY(0 == fastrpc_create_current_dsp_process());
bail:
if (err)
cleanup_current_dev();
module_put(THIS_MODULE);
}
return err;
}
static long fastrpc_device_ioctl(struct file *file, unsigned int ioctl_num,
unsigned long ioctl_param)
{
struct fastrpc_apps *me = &gfa;
struct fastrpc_ioctl_invoke invoke;
remote_arg_t *pra = 0;
void *param = (char *)ioctl_param;
int bufs, err = 0;
switch (ioctl_num) {
case FASTRPC_IOCTL_INVOKE:
VERIFY(0 == copy_from_user(&invoke, param, sizeof(invoke)));
bufs = REMOTE_SCALARS_INBUFS(invoke.sc) +
REMOTE_SCALARS_OUTBUFS(invoke.sc);
if (bufs) {
bufs = bufs * sizeof(*pra);
VERIFY(0 != (pra = kmalloc(bufs, GFP_KERNEL)));
}
VERIFY(0 == copy_from_user(pra, invoke.pra, bufs));
VERIFY(0 == (err = fastrpc_internal_invoke(me, 0, &invoke,
pra)));
break;
default:
err = -EINVAL;
break;
}
bail:
kfree(pra);
return err;
}
static const struct file_operations fops = {
.open = fastrpc_device_open,
.release = fastrpc_device_release,
.unlocked_ioctl = fastrpc_device_ioctl,
};
static int __init fastrpc_device_init(void)
{
struct fastrpc_apps *me = &gfa;
int err = 0;
VERIFY(0 == fastrpc_init());
VERIFY(0 == alloc_chrdev_region(&me->dev_no, 0, 1, DEVICE_NAME));
cdev_init(&me->cdev, &fops);
me->cdev.owner = THIS_MODULE;
VERIFY(0 == cdev_add(&me->cdev, MKDEV(MAJOR(me->dev_no), 0), 1));
pr_info("'mknod /dev/%s c %d 0'\n", DEVICE_NAME, MAJOR(me->dev_no));
bail:
return err;
}
static void __exit fastrpc_device_exit(void)
{
struct fastrpc_apps *me = &gfa;
fastrpc_deinit();
cdev_del(&me->cdev);
unregister_chrdev_region(me->dev_no, 1);
}
module_init(fastrpc_device_init);
module_exit(fastrpc_device_exit);
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef ADSPRPC_H
#define ADSPRPC_H
#include <linux/slab.h>
#include <linux/completion.h>
#include <linux/pagemap.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/list.h>
#include <linux/hash.h>
#include <linux/ion.h>
#include <mach/msm_smd.h>
#include <mach/ion.h>
#include "adsprpc_shared.h"
#ifndef ION_ADSPRPC_HEAP_ID
#define ION_ADSPRPC_HEAP_ID ION_AUDIO_HEAP_ID
#endif
#define RPC_TIMEOUT (5 * HZ)
#define RPC_HASH_BITS 5
#define RPC_HASH_SZ (1 << RPC_HASH_BITS)
#define ALIGN_8(a) ALIGN(a, 8)
#define LOCK_MMAP(kernel)\
do {\
if (!kernel)\
down_read(&current->mm->mmap_sem);\
} while (0)
#define UNLOCK_MMAP(kernel)\
do {\
if (!kernel)\
up_read(&current->mm->mmap_sem);\
} while (0)
static inline uint32_t buf_page_start(void *buf)
{
uint32_t start = (uint32_t) buf & PAGE_MASK;
return start;
}
static inline uint32_t buf_page_offset(void *buf)
{
uint32_t offset = (uint32_t) buf & (PAGE_SIZE - 1);
return offset;
}
static inline int buf_num_pages(void *buf, int len)
{
uint32_t start = buf_page_start(buf) >> PAGE_SHIFT;
uint32_t end = (((uint32_t) buf + len - 1) & PAGE_MASK) >> PAGE_SHIFT;
int nPages = end - start + 1;
return nPages;
}
static inline uint32_t buf_page_size(uint32_t size)
{
uint32_t sz = (size + (PAGE_SIZE - 1)) & PAGE_MASK;
return sz > PAGE_SIZE ? sz : PAGE_SIZE;
}
static inline int buf_get_pages(void *addr, int sz, int nr_pages, int access,
struct smq_phy_page *pages, int nr_elems)
{
struct vm_area_struct *vma;
uint32_t start = buf_page_start(addr);
uint32_t len = nr_pages << PAGE_SHIFT;
uint32_t pfn;
int n = -1, err = 0;
VERIFY(0 != access_ok(access ? VERIFY_WRITE : VERIFY_READ,
(void __user *)start, len));
VERIFY(0 != (vma = find_vma(current->mm, start)));
VERIFY(((uint32_t)addr + sz) <= vma->vm_end);
n = 0;
VERIFY(0 != (vma->vm_flags & VM_PFNMAP));
VERIFY(0 != (vma->vm_flags & VM_PFN_AT_MMAP));
VERIFY(nr_elems > 0);
pfn = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
pages->addr = __pfn_to_phys(pfn);
pages->size = len;
n++;
bail:
return n;
}
#endif

View file

@ -0,0 +1,147 @@
/*
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef ADSPRPC_SHARED_H
#define ADSPRPC_SHARED_H
#define FASTRPC_IOCTL_INVOKE _IOWR('R', 1, struct fastrpc_ioctl_invoke)
#define FASTRPC_SMD_GUID "fastrpcsmd-apps-dsp"
#define DEVICE_NAME "adsprpc-smd"
/* Retrives number of input buffers from the scalars parameter */
#define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0x0ff)
/* Retrives number of output buffers from the scalars parameter */
#define REMOTE_SCALARS_OUTBUFS(sc) (((sc) >> 8) & 0x0ff)
/* Retrives number of input handles from the scalars parameter */
#define REMOTE_SCALARS_INHANDLES(sc) (((sc) >> 4) & 0x0f)
/* Retrives number of output handles from the scalars parameter */
#define REMOTE_SCALARS_OUTHANDLES(sc) ((sc) & 0x0f)
#define REMOTE_SCALARS_LENGTH(sc) (REMOTE_SCALARS_INBUFS(sc) +\
REMOTE_SCALARS_OUTBUFS(sc) +\
REMOTE_SCALARS_INHANDLES(sc) +\
REMOTE_SCALARS_OUTHANDLES(sc))
#define REMOTE_SCALARS_MAKEX(attr, method, in, out, oin, oout) \
((((uint32_t) (attr) & 0x7) << 29) | \
(((uint32_t) (method) & 0x1f) << 24) | \
(((uint32_t) (in) & 0xff) << 16) | \
(((uint32_t) (out) & 0xff) << 8) | \
(((uint32_t) (oin) & 0x0f) << 4) | \
((uint32_t) (oout) & 0x0f))
#define REMOTE_SCALARS_MAKE(method, in, out) \
REMOTE_SCALARS_MAKEX(0, method, in, out, 0, 0)
#ifndef VERIFY_PRINT_ERROR
#define VERIFY_EPRINTF(format, args) (void)0
#endif
#ifndef VERIFY_PRINT_INFO
#define VERIFY_IPRINTF(args) (void)0
#endif
#ifndef VERIFY
#define __STR__(x) #x ":"
#define __TOSTR__(x) __STR__(x)
#define __FILE_LINE__ __FILE__ ":" __TOSTR__(__LINE__)
#define VERIFY(val) \
do {\
VERIFY_IPRINTF(__FILE_LINE__"info: calling: " #val "\n");\
if (0 == (val)) {\
err = err == 0 ? -1 : err;\
VERIFY_EPRINTF(__FILE_LINE__"error: %d: " #val "\n", err);\
goto bail;\
} else {\
VERIFY_IPRINTF(__FILE_LINE__"info: passed: " #val "\n");\
} \
} while (0)
#endif
#define remote_handle_t uint32_t
#define remote_arg_t union remote_arg
struct remote_buf {
void *pv; /* buffer pointer */
int len; /* length of buffer */
};
union remote_arg {
struct remote_buf buf; /* buffer info */
remote_handle_t h; /* remote handle */
};
struct fastrpc_ioctl_invoke {
remote_handle_t handle; /* remote handle */
uint32_t sc; /* scalars describing the data */
remote_arg_t *pra; /* remote arguments list */
};
struct smq_null_invoke {
struct smq_invoke_ctx *ctx; /* invoke caller context */
remote_handle_t handle; /* handle to invoke */
uint32_t sc; /* scalars structure describing the data */
};
struct smq_phy_page {
uint32_t addr; /* physical address */
uint32_t size; /* size of contiguous region */
};
struct smq_invoke_buf {
int num; /* number of contiguous regions */
int pgidx; /* index to start of contiguous region */
};
struct smq_invoke {
struct smq_null_invoke header;
struct smq_phy_page page; /* remote arg and list of pages address */
};
struct smq_msg {
uint32_t pid; /* process group id */
uint32_t tid; /* thread id */
struct smq_invoke invoke;
};
struct smq_invoke_rsp {
struct smq_invoke_ctx *ctx; /* invoke caller context */
int retval; /* invoke return value */
};
static inline struct smq_invoke_buf *smq_invoke_buf_start(remote_arg_t *pra,
uint32_t sc)
{
int len = REMOTE_SCALARS_LENGTH(sc);
return (struct smq_invoke_buf *)(&pra[len]);
}
static inline struct smq_phy_page *smq_phy_page_start(uint32_t sc,
struct smq_invoke_buf *buf)
{
int nTotal = REMOTE_SCALARS_INBUFS(sc) + REMOTE_SCALARS_OUTBUFS(sc);
return (struct smq_phy_page *)(&buf[nTotal]);
}
static inline int smq_invoke_buf_size(uint32_t sc, int nPages)
{
struct smq_phy_page *start = smq_phy_page_start(sc, 0);
return (int)(&(start[nPages]));
}
#endif