blob: 8ff030bd2f2f816e579cedbc6fb2c720e6c3250b [file] [log] [blame]
/*
* Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/debugfs.h>
#include "nvshm_types.h"
#include "nvshm_if.h"
#include "nvshm_priv.h"
#include "nvshm_ipc.h"
#include "nvshm_queue.h"
#include "nvshm_iobuf.h"
struct nvshm_channel *nvshm_open_channel(int chan,
struct nvshm_if_operations *ops,
void *interface_data)
{
struct nvshm_handle *handle = nvshm_get_handle();
pr_debug("%s(%d)\n", __func__, chan);
spin_lock(&handle->lock);
if (handle->chan[chan].ops) {
pr_err("%s: already registered on chan %d\n", __func__, chan);
return NULL;
}
handle->chan[chan].ops = ops;
handle->chan[chan].data = interface_data;
spin_unlock(&handle->lock);
return &handle->chan[chan];
}
void nvshm_close_channel(struct nvshm_channel *handle)
{
struct nvshm_handle *priv = nvshm_get_handle();
/* we cannot flush the work queue here as the call to
nvshm_close_channel() is made from cleanup_interfaces(),
which executes from the context of the work queue
additionally, flushing the work queue is unnecessary here
as the main work queue handler always checks the state of
the IPC */
spin_lock(&priv->lock);
priv->chan[handle->index].ops = NULL;
priv->chan[handle->index].data = NULL;
spin_unlock(&priv->lock);
}
int nvshm_write(struct nvshm_channel *handle, struct nvshm_iobuf *iob)
{
struct nvshm_handle *priv = nvshm_get_handle();
struct nvshm_iobuf *list, *leaf;
int count = 0, ret = 0;
spin_lock_bh(&priv->lock);
if (!priv->chan[handle->index].ops) {
pr_err("%s: channel not mapped\n", __func__);
spin_unlock_bh(&priv->lock);
return -EINVAL;
}
list = iob;
while (list) {
count++;
leaf = list->sg_next;
while (leaf) {
count++;
leaf = NVSHM_B2A(priv, leaf);
leaf = leaf->sg_next;
}
list = list->next;
if (list)
list = NVSHM_B2A(priv, list);
}
priv->chan[handle->index].rate_counter -= count;
if (priv->chan[handle->index].rate_counter < 0) {
priv->chan[handle->index].xoff = 1;
pr_warn("%s: rate limit hit on chan %d\n", __func__,
handle->index);
ret = 1;
}
iob->chan = handle->index;
iob->qnext = NULL;
nvshm_queue_put(priv, iob);
nvshm_generate_ipc(priv);
spin_unlock_bh(&priv->lock);
return ret;
}
/* Defered to nvshm_wq because it can be called from atomic context */
void nvshm_start_tx(struct nvshm_channel *handle)
{
struct nvshm_handle *priv = nvshm_get_handle();
queue_work(priv->nvshm_wq, &handle->start_tx_work);
}