![Peter Huewe](/assets/img/avatar_default.png)
This patch fixes the warning "Using plain integer as NULL pointer", generated by sparse, by replacing the offending 0s with NULL. If the initialization with NULL was unnecessary (due to unconditional assignment before first use) it was removed. Signed-off-by: Peter Huewe <peterhuewe@gmx.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
340 lines
9.3 KiB
C
340 lines
9.3 KiB
C
/* -----------------------------------------------------------------------------
|
|
* Copyright (c) 2011 Ozmo Inc
|
|
* Released under the GNU General Public License Version 2 (GPLv2).
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/netdevice.h>
|
|
#include "ozconfig.h"
|
|
#include "ozprotocol.h"
|
|
#include "ozeltbuf.h"
|
|
#include "ozpd.h"
|
|
#include "oztrace.h"
|
|
/*------------------------------------------------------------------------------
|
|
*/
|
|
#define OZ_ELT_INFO_MAGIC_USED 0x35791057
|
|
#define OZ_ELT_INFO_MAGIC_FREE 0x78940102
|
|
/*------------------------------------------------------------------------------
|
|
* Context: softirq-serialized
|
|
*/
|
|
int oz_elt_buf_init(struct oz_elt_buf *buf)
|
|
{
|
|
memset(buf, 0, sizeof(struct oz_elt_buf));
|
|
INIT_LIST_HEAD(&buf->stream_list);
|
|
INIT_LIST_HEAD(&buf->order_list);
|
|
INIT_LIST_HEAD(&buf->isoc_list);
|
|
buf->max_free_elts = 32;
|
|
spin_lock_init(&buf->lock);
|
|
return 0;
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
* Context: softirq or process
|
|
*/
|
|
void oz_elt_buf_term(struct oz_elt_buf *buf)
|
|
{
|
|
struct list_head *e;
|
|
int i;
|
|
/* Free any elements in the order or isoc lists. */
|
|
for (i = 0; i < 2; i++) {
|
|
struct list_head *list;
|
|
if (i)
|
|
list = &buf->order_list;
|
|
else
|
|
list = &buf->isoc_list;
|
|
e = list->next;
|
|
while (e != list) {
|
|
struct oz_elt_info *ei =
|
|
container_of(e, struct oz_elt_info, link_order);
|
|
e = e->next;
|
|
kfree(ei);
|
|
}
|
|
}
|
|
/* Free any elelment in the pool. */
|
|
while (buf->elt_pool) {
|
|
struct oz_elt_info *ei =
|
|
container_of(buf->elt_pool, struct oz_elt_info, link);
|
|
buf->elt_pool = buf->elt_pool->next;
|
|
kfree(ei);
|
|
}
|
|
buf->free_elts = 0;
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
* Context: softirq or process
|
|
*/
|
|
struct oz_elt_info *oz_elt_info_alloc(struct oz_elt_buf *buf)
|
|
{
|
|
struct oz_elt_info *ei = NULL;
|
|
spin_lock_bh(&buf->lock);
|
|
if (buf->free_elts && buf->elt_pool) {
|
|
ei = container_of(buf->elt_pool, struct oz_elt_info, link);
|
|
buf->elt_pool = ei->link.next;
|
|
buf->free_elts--;
|
|
spin_unlock_bh(&buf->lock);
|
|
if (ei->magic != OZ_ELT_INFO_MAGIC_FREE) {
|
|
oz_trace("oz_elt_info_alloc: ei with bad magic: 0x%x\n",
|
|
ei->magic);
|
|
}
|
|
} else {
|
|
spin_unlock_bh(&buf->lock);
|
|
ei = kmalloc(sizeof(struct oz_elt_info), GFP_ATOMIC);
|
|
}
|
|
if (ei) {
|
|
ei->flags = 0;
|
|
ei->app_id = 0;
|
|
ei->callback = NULL;
|
|
ei->context = 0;
|
|
ei->stream = NULL;
|
|
ei->magic = OZ_ELT_INFO_MAGIC_USED;
|
|
INIT_LIST_HEAD(&ei->link);
|
|
INIT_LIST_HEAD(&ei->link_order);
|
|
}
|
|
return ei;
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
* Precondition: oz_elt_buf.lock must be held.
|
|
* Context: softirq or process
|
|
*/
|
|
void oz_elt_info_free(struct oz_elt_buf *buf, struct oz_elt_info *ei)
|
|
{
|
|
if (ei) {
|
|
if (ei->magic == OZ_ELT_INFO_MAGIC_USED) {
|
|
buf->free_elts++;
|
|
ei->link.next = buf->elt_pool;
|
|
buf->elt_pool = &ei->link;
|
|
ei->magic = OZ_ELT_INFO_MAGIC_FREE;
|
|
} else {
|
|
oz_trace("oz_elt_info_free: bad magic ei: %p"
|
|
" magic: 0x%x\n",
|
|
ei, ei->magic);
|
|
}
|
|
}
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
* Context: softirq
|
|
*/
|
|
void oz_elt_info_free_chain(struct oz_elt_buf *buf, struct list_head *list)
|
|
{
|
|
struct list_head *e;
|
|
e = list->next;
|
|
spin_lock_bh(&buf->lock);
|
|
while (e != list) {
|
|
struct oz_elt_info *ei;
|
|
ei = container_of(e, struct oz_elt_info, link);
|
|
e = e->next;
|
|
oz_elt_info_free(buf, ei);
|
|
}
|
|
spin_unlock_bh(&buf->lock);
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
*/
|
|
int oz_elt_stream_create(struct oz_elt_buf *buf, u8 id, int max_buf_count)
|
|
{
|
|
struct oz_elt_stream *st;
|
|
|
|
oz_trace("oz_elt_stream_create(0x%x)\n", id);
|
|
|
|
st = kzalloc(sizeof(struct oz_elt_stream), GFP_ATOMIC | __GFP_ZERO);
|
|
if (st == NULL)
|
|
return -ENOMEM;
|
|
atomic_set(&st->ref_count, 1);
|
|
st->id = id;
|
|
st->max_buf_count = max_buf_count;
|
|
INIT_LIST_HEAD(&st->elt_list);
|
|
spin_lock_bh(&buf->lock);
|
|
list_add_tail(&st->link, &buf->stream_list);
|
|
spin_unlock_bh(&buf->lock);
|
|
return 0;
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
*/
|
|
int oz_elt_stream_delete(struct oz_elt_buf *buf, u8 id)
|
|
{
|
|
struct list_head *e;
|
|
struct oz_elt_stream *st = NULL;
|
|
oz_trace("oz_elt_stream_delete(0x%x)\n", id);
|
|
spin_lock_bh(&buf->lock);
|
|
e = buf->stream_list.next;
|
|
while (e != &buf->stream_list) {
|
|
st = container_of(e, struct oz_elt_stream, link);
|
|
if (st->id == id) {
|
|
list_del(e);
|
|
break;
|
|
}
|
|
st = NULL;
|
|
}
|
|
if (!st) {
|
|
spin_unlock_bh(&buf->lock);
|
|
return -1;
|
|
}
|
|
e = st->elt_list.next;
|
|
while (e != &st->elt_list) {
|
|
struct oz_elt_info *ei =
|
|
container_of(e, struct oz_elt_info, link);
|
|
e = e->next;
|
|
list_del_init(&ei->link);
|
|
list_del_init(&ei->link_order);
|
|
st->buf_count -= ei->length;
|
|
oz_trace2(OZ_TRACE_STREAM, "Stream down: %d %d %d\n",
|
|
st->buf_count,
|
|
ei->length, atomic_read(&st->ref_count));
|
|
oz_elt_stream_put(st);
|
|
oz_elt_info_free(buf, ei);
|
|
}
|
|
spin_unlock_bh(&buf->lock);
|
|
oz_elt_stream_put(st);
|
|
return 0;
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
*/
|
|
void oz_elt_stream_get(struct oz_elt_stream *st)
|
|
{
|
|
atomic_inc(&st->ref_count);
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
*/
|
|
void oz_elt_stream_put(struct oz_elt_stream *st)
|
|
{
|
|
if (atomic_dec_and_test(&st->ref_count)) {
|
|
oz_trace("Stream destroyed\n");
|
|
kfree(st);
|
|
}
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
* Precondition: Element buffer lock must be held.
|
|
* If this function fails the caller is responsible for deallocating the elt
|
|
* info structure.
|
|
*/
|
|
int oz_queue_elt_info(struct oz_elt_buf *buf, u8 isoc, u8 id,
|
|
struct oz_elt_info *ei)
|
|
{
|
|
struct oz_elt_stream *st = NULL;
|
|
struct list_head *e;
|
|
if (id) {
|
|
list_for_each(e, &buf->stream_list) {
|
|
st = container_of(e, struct oz_elt_stream, link);
|
|
if (st->id == id)
|
|
break;
|
|
}
|
|
if (e == &buf->stream_list) {
|
|
/* Stream specified but stream not known so fail.
|
|
* Caller deallocates element info. */
|
|
return -1;
|
|
}
|
|
}
|
|
if (st) {
|
|
/* If this is an ISOC fixed element that needs a frame number
|
|
* then insert that now. Earlier we stored the unit count in
|
|
* this field.
|
|
*/
|
|
struct oz_isoc_fixed *body = (struct oz_isoc_fixed *)
|
|
&ei->data[sizeof(struct oz_elt)];
|
|
if ((body->app_id == OZ_APPID_USB) && (body->type
|
|
== OZ_USB_ENDPOINT_DATA) &&
|
|
(body->format == OZ_DATA_F_ISOC_FIXED)) {
|
|
u8 unit_count = body->frame_number;
|
|
body->frame_number = st->frame_number;
|
|
st->frame_number += unit_count;
|
|
}
|
|
/* Claim stream and update accounts */
|
|
oz_elt_stream_get(st);
|
|
ei->stream = st;
|
|
st->buf_count += ei->length;
|
|
/* Add to list in stream. */
|
|
list_add_tail(&ei->link, &st->elt_list);
|
|
oz_trace2(OZ_TRACE_STREAM, "Stream up: %d %d\n",
|
|
st->buf_count, ei->length);
|
|
/* Check if we have too much buffered for this stream. If so
|
|
* start dropping elements until we are back in bounds.
|
|
*/
|
|
while ((st->buf_count > st->max_buf_count) &&
|
|
!list_empty(&st->elt_list)) {
|
|
struct oz_elt_info *ei2 =
|
|
list_first_entry(&st->elt_list,
|
|
struct oz_elt_info, link);
|
|
list_del_init(&ei2->link);
|
|
list_del_init(&ei2->link_order);
|
|
st->buf_count -= ei2->length;
|
|
oz_elt_info_free(buf, ei2);
|
|
oz_elt_stream_put(st);
|
|
}
|
|
}
|
|
list_add_tail(&ei->link_order, isoc ?
|
|
&buf->isoc_list : &buf->order_list);
|
|
return 0;
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
*/
|
|
int oz_select_elts_for_tx(struct oz_elt_buf *buf, u8 isoc, unsigned *len,
|
|
unsigned max_len, struct list_head *list)
|
|
{
|
|
int count = 0;
|
|
struct list_head *e;
|
|
struct list_head *el;
|
|
struct oz_elt_info *ei;
|
|
spin_lock_bh(&buf->lock);
|
|
if (isoc)
|
|
el = &buf->isoc_list;
|
|
else
|
|
el = &buf->order_list;
|
|
e = el->next;
|
|
while (e != el) {
|
|
struct oz_app_hdr *app_hdr;
|
|
ei = container_of(e, struct oz_elt_info, link_order);
|
|
e = e->next;
|
|
if ((*len + ei->length) <= max_len) {
|
|
app_hdr = (struct oz_app_hdr *)
|
|
&ei->data[sizeof(struct oz_elt)];
|
|
app_hdr->elt_seq_num = buf->tx_seq_num[ei->app_id]++;
|
|
if (buf->tx_seq_num[ei->app_id] == 0)
|
|
buf->tx_seq_num[ei->app_id] = 1;
|
|
*len += ei->length;
|
|
list_del(&ei->link);
|
|
list_del(&ei->link_order);
|
|
if (ei->stream) {
|
|
ei->stream->buf_count -= ei->length;
|
|
oz_trace2(OZ_TRACE_STREAM,
|
|
"Stream down: %d %d\n",
|
|
ei->stream->buf_count, ei->length);
|
|
oz_elt_stream_put(ei->stream);
|
|
ei->stream = NULL;
|
|
}
|
|
INIT_LIST_HEAD(&ei->link_order);
|
|
list_add_tail(&ei->link, list);
|
|
count++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock_bh(&buf->lock);
|
|
return count;
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
*/
|
|
int oz_are_elts_available(struct oz_elt_buf *buf)
|
|
{
|
|
return buf->order_list.next != &buf->order_list;
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
*/
|
|
void oz_trim_elt_pool(struct oz_elt_buf *buf)
|
|
{
|
|
struct list_head *free = NULL;
|
|
struct list_head *e;
|
|
spin_lock_bh(&buf->lock);
|
|
while (buf->free_elts > buf->max_free_elts) {
|
|
e = buf->elt_pool;
|
|
buf->elt_pool = e->next;
|
|
e->next = free;
|
|
free = e;
|
|
buf->free_elts--;
|
|
}
|
|
spin_unlock_bh(&buf->lock);
|
|
while (free) {
|
|
struct oz_elt_info *ei =
|
|
container_of(free, struct oz_elt_info, link);
|
|
free = free->next;
|
|
kfree(ei);
|
|
}
|
|
}
|