kernel_samsung_a34x-permissive/drivers/misc/tzdev/lib/circ_buf_packet.c

147 lines
3.6 KiB
C
Raw Normal View History

/*
* lib/circ_buf_packet.c
*
* Copyright (C) 2012-2017, Samsung Electronics Co., Ltd.
*
* Circular buffer packet operations.
*/
#include <linux/err.h>
#include "lib/circ_buf.h"
#include "lib/circ_buf_packet.h"
struct circ_buf_packet_header {
uint32_t packet_size;
} __packed;
size_t circ_buf_size_for_packet(size_t size)
{
return size + sizeof(struct circ_buf_packet_header);
}
ssize_t circ_buf_write_packet(struct circ_buf_desc *circ_buf_desc, const char *buf,
size_t length, enum circ_buf_user_mode mode)
{
ssize_t ret;
ret = circ_buf_write_packet_local(circ_buf_desc, buf, length, mode);
if (!IS_ERR_VALUE(ret))
circ_buf_flush_write(circ_buf_desc);
return ret;
}
ssize_t circ_buf_write_packet_local(struct circ_buf_desc *circ_buf_desc, const char *buf,
size_t length, enum circ_buf_user_mode mode)
{
struct circ_buf_packet_header header;
ssize_t ret;
unsigned int write_count_orig = circ_buf_desc->write_count;
header.packet_size = length;
ret = circ_buf_write_local(circ_buf_desc, (void *) &header,
sizeof(struct circ_buf_packet_header), CIRC_BUF_MODE_KERNEL);
if (IS_ERR_VALUE(ret))
return ret;
ret = circ_buf_write_local(circ_buf_desc, buf, length, mode);
if (IS_ERR_VALUE(ret))
circ_buf_desc->write_count = write_count_orig;
return ret;
}
ssize_t circ_buf_read_packet(struct circ_buf_desc *circ_buf_desc, char *buf,
size_t length, enum circ_buf_user_mode mode)
{
ssize_t ret;
ret = circ_buf_read_packet_local(circ_buf_desc, buf, length, mode);
if (!IS_ERR_VALUE(ret))
circ_buf_flush_read(circ_buf_desc);
return ret;
}
ssize_t circ_buf_read_packet_local(struct circ_buf_desc *circ_buf_desc, char *buf,
size_t length, enum circ_buf_user_mode mode)
{
struct circ_buf_packet_header header;
ssize_t ret;
size_t packet_size;
unsigned int read_count_orig = circ_buf_desc->read_count;
ret = circ_buf_read_local(circ_buf_desc, (void *) &header,
sizeof(struct circ_buf_packet_header), CIRC_BUF_MODE_KERNEL);
if (IS_ERR_VALUE(ret))
return ret;
packet_size = header.packet_size;
if (packet_size > length) {
circ_buf_desc->read_count = read_count_orig;
return -EMSGSIZE;
}
ret = circ_buf_read_local(circ_buf_desc, buf, packet_size, mode);
if (IS_ERR_VALUE(ret))
circ_buf_desc->read_count = read_count_orig;
return ret;
}
ssize_t circ_buf_drop_packet(struct circ_buf_desc *circ_buf_desc)
{
ssize_t ret;
ret = circ_buf_drop_packet_local(circ_buf_desc);
if (!IS_ERR_VALUE(ret))
circ_buf_flush_read(circ_buf_desc);
return ret;
}
ssize_t circ_buf_drop_packet_local(struct circ_buf_desc *circ_buf_desc)
{
struct circ_buf_packet_header header;
ssize_t ret;
unsigned int read_count_orig = circ_buf_desc->read_count;
ret = circ_buf_read_local(circ_buf_desc, (void *) &header,
sizeof(struct circ_buf_packet_header), CIRC_BUF_MODE_KERNEL);
if (IS_ERR_VALUE(ret))
return ret;
ret = circ_buf_read_local(circ_buf_desc, NULL, header.packet_size,
CIRC_BUF_MODE_DROP);
if (IS_ERR_VALUE(ret))
circ_buf_desc->read_count = read_count_orig;
return ret;
}
ssize_t circ_buf_peek_packet(struct circ_buf_desc *circ_buf_desc, char *buf,
size_t length, size_t offset, enum circ_buf_user_mode mode)
{
struct circ_buf_packet_header header;
ssize_t ret;
unsigned int read_count_orig = circ_buf_desc->read_count;
ret = circ_buf_read_local(circ_buf_desc, (void *) &header,
sizeof(struct circ_buf_packet_header), CIRC_BUF_MODE_KERNEL);
if (IS_ERR_VALUE(ret))
return ret;
if (offset + length > header.packet_size) {
ret = -EMSGSIZE;
goto out;
}
ret = __circ_buf_read_local(circ_buf_desc, buf, length, offset, mode);
out:
circ_buf_desc->read_count = read_count_orig;
return ret;
}