blob: 76ce0c049011aed639412c2d44cefa5ed1a38b52 [file] [log] [blame]
/* Copyright (c) 2012-2013, 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 <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include "mpq_dvb_debug.h"
#include "mpq_stream_buffer.h"
int mpq_streambuffer_init(
struct mpq_streambuffer *sbuff,
enum mpq_streambuffer_mode mode,
struct mpq_streambuffer_buffer_desc *data_buffers,
u32 data_buff_num,
void *packet_buff,
size_t packet_buff_size)
{
if ((NULL == sbuff) || (NULL == data_buffers) ||
(NULL == packet_buff) || (data_buff_num == 0))
return -EINVAL;
if (data_buff_num > 1) {
if (mode != MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR)
return -EINVAL;
/* Linear buffer group */
dvb_ringbuffer_init(
&sbuff->raw_data,
data_buffers,
data_buff_num *
sizeof(struct mpq_streambuffer_buffer_desc));
} else {
if (mode != MPQ_STREAMBUFFER_BUFFER_MODE_RING)
return -EINVAL;
/* Single ring-buffer */
dvb_ringbuffer_init(&sbuff->raw_data,
data_buffers[0].base, data_buffers[0].size);
}
sbuff->mode = mode;
sbuff->buffers = data_buffers;
sbuff->pending_buffers_count = 0;
sbuff->buffers_num = data_buff_num;
dvb_ringbuffer_init(&sbuff->packet_data, packet_buff, packet_buff_size);
return 0;
}
EXPORT_SYMBOL(mpq_streambuffer_init);
void mpq_streambuffer_terminate(struct mpq_streambuffer *sbuff)
{
spin_lock(&sbuff->packet_data.lock);
spin_lock(&sbuff->raw_data.lock);
sbuff->packet_data.error = -ENODEV;
sbuff->raw_data.error = -ENODEV;
spin_unlock(&sbuff->raw_data.lock);
spin_unlock(&sbuff->packet_data.lock);
wake_up_all(&sbuff->raw_data.queue);
wake_up_all(&sbuff->packet_data.queue);
}
EXPORT_SYMBOL(mpq_streambuffer_terminate);
ssize_t mpq_streambuffer_pkt_next(
struct mpq_streambuffer *sbuff,
ssize_t idx, size_t *pktlen)
{
ssize_t packet_idx;
spin_lock(&sbuff->packet_data.lock);
/* buffer was released, return no packet available */
if (sbuff->packet_data.error == -ENODEV) {
spin_unlock(&sbuff->packet_data.lock);
return -ENODEV;
}
packet_idx = dvb_ringbuffer_pkt_next(&sbuff->packet_data, idx, pktlen);
spin_unlock(&sbuff->packet_data.lock);
return packet_idx;
}
EXPORT_SYMBOL(mpq_streambuffer_pkt_next);
ssize_t mpq_streambuffer_pkt_read(
struct mpq_streambuffer *sbuff,
size_t idx,
struct mpq_streambuffer_packet_header *packet,
u8 *user_data)
{
size_t ret;
size_t read_len;
spin_lock(&sbuff->packet_data.lock);
/* buffer was released, return no packet available */
if (sbuff->packet_data.error == -ENODEV) {
spin_unlock(&sbuff->packet_data.lock);
return -ENODEV;
}
/* read-out the packet header first */
ret = dvb_ringbuffer_pkt_read(
&sbuff->packet_data, idx, 0,
(u8 *)packet,
sizeof(struct mpq_streambuffer_packet_header));
/* verify length, at least packet header should exist */
if (ret != sizeof(struct mpq_streambuffer_packet_header)) {
spin_unlock(&sbuff->packet_data.lock);
return -EINVAL;
}
read_len = ret;
/* read-out private user-data if there are such */
if ((packet->user_data_len) && (user_data != NULL)) {
ret = dvb_ringbuffer_pkt_read(
&sbuff->packet_data,
idx,
sizeof(struct mpq_streambuffer_packet_header),
user_data,
packet->user_data_len);
if (ret < 0) {
spin_unlock(&sbuff->packet_data.lock);
return ret;
}
read_len += ret;
}
spin_unlock(&sbuff->packet_data.lock);
return read_len;
}
EXPORT_SYMBOL(mpq_streambuffer_pkt_read);
int mpq_streambuffer_pkt_dispose(
struct mpq_streambuffer *sbuff,
size_t idx,
int dispose_data)
{
int ret;
struct mpq_streambuffer_packet_header packet;
if (NULL == sbuff)
return -EINVAL;
spin_lock(&sbuff->packet_data.lock);
/* check if buffer was released */
if (sbuff->packet_data.error == -ENODEV) {
spin_unlock(&sbuff->packet_data.lock);
return -ENODEV;
}
/* read-out the packet header first */
ret = dvb_ringbuffer_pkt_read(&sbuff->packet_data, idx,
0,
(u8 *)&packet,
sizeof(struct mpq_streambuffer_packet_header));
spin_unlock(&sbuff->packet_data.lock);
if (ret != sizeof(struct mpq_streambuffer_packet_header))
return -EINVAL;
if ((MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR == sbuff->mode) ||
(dispose_data)) {
/* Advance the read pointer in the raw-data buffer first */
ret = mpq_streambuffer_data_read_dispose(sbuff,
packet.raw_data_len);
if (ret != 0)
return ret;
}
spin_lock(&sbuff->packet_data.lock);
spin_lock(&sbuff->raw_data.lock);
/* check if buffer was released */
if ((sbuff->packet_data.error == -ENODEV) ||
(sbuff->raw_data.error == -ENODEV)) {
spin_unlock(&sbuff->raw_data.lock);
spin_unlock(&sbuff->packet_data.lock);
return -ENODEV;
}
/* Move read pointer to the next linear buffer for subsequent reads */
if ((MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR == sbuff->mode) &&
(packet.raw_data_len > 0)) {
struct mpq_streambuffer_buffer_desc *desc;
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pread];
desc->write_ptr = 0;
desc->read_ptr = 0;
DVB_RINGBUFFER_SKIP(&sbuff->raw_data,
sizeof(struct mpq_streambuffer_buffer_desc));
sbuff->pending_buffers_count--;
wake_up_all(&sbuff->raw_data.queue);
}
/* Now clear the packet from the packet header */
dvb_ringbuffer_pkt_dispose(&sbuff->packet_data, idx);
spin_unlock(&sbuff->raw_data.lock);
spin_unlock(&sbuff->packet_data.lock);
if (sbuff->cb)
sbuff->cb(sbuff, sbuff->cb_user_data);
return 0;
}
EXPORT_SYMBOL(mpq_streambuffer_pkt_dispose);
int mpq_streambuffer_pkt_write(
struct mpq_streambuffer *sbuff,
struct mpq_streambuffer_packet_header *packet,
u8 *user_data)
{
ssize_t idx;
size_t len;
if ((NULL == sbuff) || (NULL == packet))
return -EINVAL;
spin_lock(&sbuff->packet_data.lock);
/* check if buffer was released */
if (sbuff->packet_data.error == -ENODEV) {
spin_unlock(&sbuff->packet_data.lock);
return -ENODEV;
}
len = sizeof(struct mpq_streambuffer_packet_header) +
packet->user_data_len;
/* Make sure enough space available for packet header */
if (dvb_ringbuffer_free(&sbuff->packet_data) < len) {
spin_unlock(&sbuff->packet_data.lock);
return -ENOSPC;
}
/* Starting writing packet header */
idx = dvb_ringbuffer_pkt_start(&sbuff->packet_data, len);
/* Write non-user private data header */
dvb_ringbuffer_write(&sbuff->packet_data,
(u8 *)packet,
sizeof(struct mpq_streambuffer_packet_header));
/* Write user's own private data header */
dvb_ringbuffer_write(&sbuff->packet_data,
user_data,
packet->user_data_len);
dvb_ringbuffer_pkt_close(&sbuff->packet_data, idx);
/* Move write pointer to next linear buffer for subsequent writes */
if ((MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR == sbuff->mode) &&
(packet->raw_data_len > 0)) {
if (sbuff->pending_buffers_count == sbuff->buffers_num) {
spin_unlock(&sbuff->packet_data.lock);
return -ENOSPC;
}
DVB_RINGBUFFER_PUSH(&sbuff->raw_data,
sizeof(struct mpq_streambuffer_buffer_desc));
sbuff->pending_buffers_count++;
}
spin_unlock(&sbuff->packet_data.lock);
wake_up_all(&sbuff->packet_data.queue);
return 0;
}
EXPORT_SYMBOL(mpq_streambuffer_pkt_write);
ssize_t mpq_streambuffer_data_write(
struct mpq_streambuffer *sbuff,
const u8 *buf, size_t len)
{
int res;
if ((NULL == sbuff) || (NULL == buf))
return -EINVAL;
spin_lock(&sbuff->raw_data.lock);
/* check if buffer was released */
if (sbuff->raw_data.error == -ENODEV) {
spin_unlock(&sbuff->raw_data.lock);
return -ENODEV;
}
if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len)) {
spin_unlock(&sbuff->raw_data.lock);
return -ENOSPC;
}
/*
* Secure buffers are not permitted to be mapped into kernel
* memory, and so buffer base address may be NULL
*/
if (NULL == sbuff->raw_data.data) {
spin_unlock(&sbuff->raw_data.lock);
return -EPERM;
}
res = dvb_ringbuffer_write(&sbuff->raw_data, buf, len);
wake_up_all(&sbuff->raw_data.queue);
} else {
/* Linear buffer group */
struct mpq_streambuffer_buffer_desc *desc;
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pwrite];
/*
* Secure buffers are not permitted to be mapped into kernel
* memory, and so buffer base address may be NULL
*/
if (NULL == desc->base) {
spin_unlock(&sbuff->raw_data.lock);
return -EPERM;
}
if ((sbuff->pending_buffers_count == sbuff->buffers_num) ||
((desc->size - desc->write_ptr) < len)) {
MPQ_DVB_ERR_PRINT(
"%s: No space available! %d pending buffers out of %d total buffers. write_ptr=%d, size=%d\n",
__func__,
sbuff->pending_buffers_count,
sbuff->buffers_num,
desc->write_ptr,
desc->size);
spin_unlock(&sbuff->raw_data.lock);
return -ENOSPC;
}
memcpy(desc->base + desc->write_ptr, buf, len);
desc->write_ptr += len;
res = len;
}
spin_unlock(&sbuff->raw_data.lock);
return res;
}
EXPORT_SYMBOL(mpq_streambuffer_data_write);
int mpq_streambuffer_data_write_deposit(
struct mpq_streambuffer *sbuff,
size_t len)
{
if (NULL == sbuff)
return -EINVAL;
spin_lock(&sbuff->raw_data.lock);
/* check if buffer was released */
if (sbuff->raw_data.error == -ENODEV) {
spin_unlock(&sbuff->raw_data.lock);
return -ENODEV;
}
if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len)) {
spin_unlock(&sbuff->raw_data.lock);
return -ENOSPC;
}
DVB_RINGBUFFER_PUSH(&sbuff->raw_data, len);
wake_up_all(&sbuff->raw_data.queue);
} else {
/* Linear buffer group */
struct mpq_streambuffer_buffer_desc *desc;
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pwrite];
if ((sbuff->pending_buffers_count == sbuff->buffers_num) ||
((desc->size - desc->write_ptr) < len)) {
MPQ_DVB_ERR_PRINT(
"%s: No space available!\n",
__func__);
spin_unlock(&sbuff->raw_data.lock);
return -ENOSPC;
}
desc->write_ptr += len;
}
spin_unlock(&sbuff->raw_data.lock);
return 0;
}
EXPORT_SYMBOL(mpq_streambuffer_data_write_deposit);
ssize_t mpq_streambuffer_data_read(
struct mpq_streambuffer *sbuff,
u8 *buf, size_t len)
{
ssize_t actual_len = 0;
if ((NULL == sbuff) || (NULL == buf))
return -EINVAL;
spin_lock(&sbuff->raw_data.lock);
/* check if buffer was released */
if (sbuff->raw_data.error == -ENODEV) {
spin_unlock(&sbuff->raw_data.lock);
return -ENODEV;
}
if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
/*
* Secure buffers are not permitted to be mapped into kernel
* memory, and so buffer base address may be NULL
*/
if (NULL == sbuff->raw_data.data) {
spin_unlock(&sbuff->raw_data.lock);
return -EPERM;
}
actual_len = dvb_ringbuffer_avail(&sbuff->raw_data);
if (actual_len < len)
len = actual_len;
if (len)
dvb_ringbuffer_read(&sbuff->raw_data, buf, len);
wake_up_all(&sbuff->raw_data.queue);
} else {
/* Linear buffer group */
struct mpq_streambuffer_buffer_desc *desc;
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pread];
/*
* Secure buffers are not permitted to be mapped into kernel
* memory, and so buffer base address may be NULL
*/
if (NULL == desc->base) {
spin_unlock(&sbuff->raw_data.lock);
return -EPERM;
}
actual_len = (desc->write_ptr - desc->read_ptr);
if (actual_len < len)
len = actual_len;
memcpy(buf, desc->base + desc->read_ptr, len);
desc->read_ptr += len;
}
spin_unlock(&sbuff->raw_data.lock);
return len;
}
EXPORT_SYMBOL(mpq_streambuffer_data_read);
ssize_t mpq_streambuffer_data_read_user(
struct mpq_streambuffer *sbuff,
u8 __user *buf, size_t len)
{
ssize_t actual_len = 0;
if ((NULL == sbuff) || (NULL == buf))
return -EINVAL;
/* check if buffer was released */
if (sbuff->raw_data.error == -ENODEV)
return -ENODEV;
if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
/*
* Secure buffers are not permitted to be mapped into kernel
* memory, and so buffer base address may be NULL
*/
if (NULL == sbuff->raw_data.data)
return -EPERM;
actual_len = dvb_ringbuffer_avail(&sbuff->raw_data);
if (actual_len < len)
len = actual_len;
if (len)
dvb_ringbuffer_read_user(&sbuff->raw_data, buf, len);
wake_up_all(&sbuff->raw_data.queue);
} else {
/* Linear buffer group */
struct mpq_streambuffer_buffer_desc *desc;
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pread];
/*
* Secure buffers are not permitted to be mapped into kernel
* memory, and so buffer base address may be NULL
*/
if (NULL == desc->base)
return -EPERM;
actual_len = (desc->write_ptr - desc->read_ptr);
if (actual_len < len)
len = actual_len;
if (copy_to_user(buf, desc->base + desc->read_ptr, len))
return -EFAULT;
desc->read_ptr += len;
}
return len;
}
EXPORT_SYMBOL(mpq_streambuffer_data_read_user);
int mpq_streambuffer_data_read_dispose(
struct mpq_streambuffer *sbuff,
size_t len)
{
if (NULL == sbuff)
return -EINVAL;
spin_lock(&sbuff->raw_data.lock);
/* check if buffer was released */
if (sbuff->raw_data.error == -ENODEV) {
spin_unlock(&sbuff->raw_data.lock);
return -ENODEV;
}
if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
if (unlikely(dvb_ringbuffer_avail(&sbuff->raw_data) < len)) {
spin_unlock(&sbuff->raw_data.lock);
return -EINVAL;
}
DVB_RINGBUFFER_SKIP(&sbuff->raw_data, len);
wake_up_all(&sbuff->raw_data.queue);
} else {
struct mpq_streambuffer_buffer_desc *desc;
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pread];
if ((desc->read_ptr + len) > desc->size)
desc->read_ptr = desc->size;
else
desc->read_ptr += len;
}
spin_unlock(&sbuff->raw_data.lock);
return 0;
}
EXPORT_SYMBOL(mpq_streambuffer_data_read_dispose);
int mpq_streambuffer_get_buffer_handle(
struct mpq_streambuffer *sbuff,
int read_buffer,
int *handle)
{
struct mpq_streambuffer_buffer_desc *desc = NULL;
if ((NULL == sbuff) || (NULL == handle))
return -EINVAL;
spin_lock(&sbuff->raw_data.lock);
/* check if buffer was released */
if (sbuff->raw_data.error == -ENODEV) {
spin_unlock(&sbuff->raw_data.lock);
return -ENODEV;
}
if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
*handle = sbuff->buffers[0].handle;
} else {
if (read_buffer)
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pread];
else
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pwrite];
*handle = desc->handle;
}
spin_unlock(&sbuff->raw_data.lock);
return 0;
}
EXPORT_SYMBOL(mpq_streambuffer_get_buffer_handle);
int mpq_streambuffer_register_pkt_dispose(
struct mpq_streambuffer *sbuff,
mpq_streambuffer_pkt_dispose_cb cb_func,
void *user_data)
{
if ((NULL == sbuff) || (NULL == cb_func))
return -EINVAL;
sbuff->cb = cb_func;
sbuff->cb_user_data = user_data;
return 0;
}
EXPORT_SYMBOL(mpq_streambuffer_register_pkt_dispose);
ssize_t mpq_streambuffer_data_free(
struct mpq_streambuffer *sbuff)
{
struct mpq_streambuffer_buffer_desc *desc;
if (NULL == sbuff)
return -EINVAL;
spin_lock(&sbuff->raw_data.lock);
/* check if buffer was released */
if (sbuff->raw_data.error == -ENODEV) {
spin_unlock(&sbuff->raw_data.lock);
return -ENODEV;
}
if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
spin_unlock(&sbuff->raw_data.lock);
return dvb_ringbuffer_free(&sbuff->raw_data);
}
if (sbuff->pending_buffers_count == sbuff->buffers_num) {
spin_unlock(&sbuff->raw_data.lock);
return 0;
}
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pwrite];
spin_unlock(&sbuff->raw_data.lock);
return desc->size - desc->write_ptr;
}
EXPORT_SYMBOL(mpq_streambuffer_data_free);
ssize_t mpq_streambuffer_data_avail(
struct mpq_streambuffer *sbuff)
{
struct mpq_streambuffer_buffer_desc *desc;
if (NULL == sbuff)
return -EINVAL;
spin_lock(&sbuff->raw_data.lock);
/* check if buffer was released */
if (sbuff->raw_data.error == -ENODEV) {
spin_unlock(&sbuff->raw_data.lock);
return -ENODEV;
}
if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
ssize_t avail = dvb_ringbuffer_avail(&sbuff->raw_data);
spin_unlock(&sbuff->raw_data.lock);
return avail;
}
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pread];
spin_unlock(&sbuff->raw_data.lock);
return desc->write_ptr - desc->read_ptr;
}
EXPORT_SYMBOL(mpq_streambuffer_data_avail);
int mpq_streambuffer_get_data_rw_offset(
struct mpq_streambuffer *sbuff,
u32 *read_offset,
u32 *write_offset)
{
if (NULL == sbuff)
return -EINVAL;
spin_lock(&sbuff->raw_data.lock);
/* check if buffer was released */
if (sbuff->raw_data.error == -ENODEV) {
spin_unlock(&sbuff->raw_data.lock);
return -ENODEV;
}
if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
if (read_offset)
*read_offset = sbuff->raw_data.pread;
if (write_offset)
*write_offset = sbuff->raw_data.pwrite;
} else {
struct mpq_streambuffer_buffer_desc *desc;
if (read_offset) {
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pread];
*read_offset = desc->read_ptr;
}
if (write_offset) {
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pwrite];
*write_offset = desc->write_ptr;
}
}
spin_unlock(&sbuff->raw_data.lock);
return 0;
}
EXPORT_SYMBOL(mpq_streambuffer_get_data_rw_offset);