Works with devices using Ozmo USB over WiFi technology. See README file for further details. Added third batch of files to drivers/staging/ozwpan. Signed-off-by: Chris Kelly <ckelly@xxxxxxxxxxxxxxx> --- drivers/staging/ozwpan/ozeltbuf.c | 339 ++++++++++++++++++++++++++++++++ drivers/staging/ozwpan/ozeltbuf.h | 70 +++++++ drivers/staging/ozwpan/ozevent.c | 116 +++++++++++ drivers/staging/ozwpan/ozevent.h | 31 +++ drivers/staging/ozwpan/ozurbparanoia.c | 53 +++++ drivers/staging/ozwpan/ozurbparanoia.h | 19 ++ 6 files changed, 628 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/ozwpan/ozeltbuf.c create mode 100644 drivers/staging/ozwpan/ozeltbuf.h create mode 100644 drivers/staging/ozwpan/ozevent.c create mode 100644 drivers/staging/ozwpan/ozevent.h create mode 100644 drivers/staging/ozwpan/ozurbparanoia.c create mode 100644 drivers/staging/ozwpan/ozurbparanoia.h diff --git a/drivers/staging/ozwpan/ozeltbuf.c b/drivers/staging/ozwpan/ozeltbuf.c new file mode 100644 index 0000000..1c96c00 --- /dev/null +++ b/drivers/staging/ozwpan/ozeltbuf.c @@ -0,0 +1,339 @@ +/* ----------------------------------------------------------------------------- + * 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" +#include "ozalloc.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; + oz_free(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; + oz_free(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 = 0; + 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 = oz_alloc(sizeof(struct oz_elt_info), GFP_ATOMIC); + } + if (ei) { + ei->flags = 0; + ei->app_id = 0; + ei->callback = 0; + ei->context = 0; + ei->stream = 0; + 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_alloc(sizeof(struct oz_elt_stream), GFP_ATOMIC | __GFP_ZERO); + oz_trace("oz_elt_stream_create(0x%x)\n", id); + if (st == 0) + return -1; + memset(st, 0, sizeof(struct oz_elt_stream)); + 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; + 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 = 0; + } + 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"); + oz_free(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 = 0; + 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 = 0; + } + 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 = 0; + 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; + oz_free(ei); + } +} diff --git a/drivers/staging/ozwpan/ozeltbuf.h b/drivers/staging/ozwpan/ozeltbuf.h new file mode 100644 index 0000000..03c12f5 --- /dev/null +++ b/drivers/staging/ozwpan/ozeltbuf.h @@ -0,0 +1,70 @@ +/* ----------------------------------------------------------------------------- + * Copyright (c) 2011 Ozmo Inc + * Released under the GNU General Public License Version 2 (GPLv2). + * ----------------------------------------------------------------------------- + */ +#ifndef _OZELTBUF_H +#define _OZELTBUF_H + +#include "ozprotocol.h" + +/*----------------------------------------------------------------------------- + */ +struct oz_pd; +typedef void (*oz_elt_callback_t)(struct oz_pd *pd, long context); + +struct oz_elt_stream { + struct list_head link; + struct list_head elt_list; + atomic_t ref_count; + unsigned buf_count; + unsigned max_buf_count; + u8 frame_number; + u8 id; +}; + +#define OZ_MAX_ELT_PAYLOAD 255 +struct oz_elt_info { + struct list_head link; + struct list_head link_order; + u8 flags; + u8 app_id; + oz_elt_callback_t callback; + long context; + struct oz_elt_stream *stream; + u8 data[sizeof(struct oz_elt) + OZ_MAX_ELT_PAYLOAD]; + int length; + unsigned magic; +}; +/* Flags values */ +#define OZ_EI_F_MARKED 0x1 + +struct oz_elt_buf { + spinlock_t lock; + struct list_head stream_list; + struct list_head order_list; + struct list_head isoc_list; + struct list_head *elt_pool; + int free_elts; + int max_free_elts; + u8 tx_seq_num[OZ_NB_APPS]; +}; + +int oz_elt_buf_init(struct oz_elt_buf *buf); +void oz_elt_buf_term(struct oz_elt_buf *buf); +struct oz_elt_info *oz_elt_info_alloc(struct oz_elt_buf *buf); +void oz_elt_info_free(struct oz_elt_buf *buf, struct oz_elt_info *ei); +void oz_elt_info_free_chain(struct oz_elt_buf *buf, struct list_head *list); +int oz_elt_stream_create(struct oz_elt_buf *buf, u8 id, int max_buf_count); +int oz_elt_stream_delete(struct oz_elt_buf *buf, u8 id); +void oz_elt_stream_get(struct oz_elt_stream *st); +void oz_elt_stream_put(struct oz_elt_stream *st); +int oz_queue_elt_info(struct oz_elt_buf *buf, u8 isoc, u8 id, + struct oz_elt_info *ei); +int oz_select_elts_for_tx(struct oz_elt_buf *buf, u8 isoc, unsigned *len, + unsigned max_len, struct list_head *list); +int oz_are_elts_available(struct oz_elt_buf *buf); +void oz_trim_elt_pool(struct oz_elt_buf *buf); + +#endif /* _OZELTBUF_H */ + diff --git a/drivers/staging/ozwpan/ozevent.c b/drivers/staging/ozwpan/ozevent.c new file mode 100644 index 0000000..73703d3 --- /dev/null +++ b/drivers/staging/ozwpan/ozevent.c @@ -0,0 +1,116 @@ +/* ----------------------------------------------------------------------------- + * Copyright (c) 2011 Ozmo Inc + * Released under the GNU General Public License Version 2 (GPLv2). + * ----------------------------------------------------------------------------- + */ +#include "ozconfig.h" +#ifdef WANT_EVENT_TRACE +#include <linux/jiffies.h> +#include <linux/uaccess.h> +#include "oztrace.h" +#include "ozevent.h" +/*------------------------------------------------------------------------------ + */ +unsigned long g_evt_mask = 0xffffffff; +/*------------------------------------------------------------------------------ + */ +#define OZ_MAX_EVTS 2048 /* Must be power of 2 */ +DEFINE_SPINLOCK(g_eventlock); +static int g_evt_in; +static int g_evt_out; +static int g_missed_events; +static struct oz_event g_events[OZ_MAX_EVTS]; +/*------------------------------------------------------------------------------ + * Context: process + */ +void oz_event_init(void) +{ + oz_trace("Event tracing initialized\n"); + g_evt_in = g_evt_out = 0; + g_missed_events = 0; +} +/*------------------------------------------------------------------------------ + * Context: process + */ +void oz_event_term(void) +{ + oz_trace("Event tracing terminated\n"); +} +/*------------------------------------------------------------------------------ + * Context: any + */ +void oz_event_log2(u8 evt, u8 ctx1, u16 ctx2, void *ctx3, unsigned ctx4) +{ + unsigned long irqstate; + int ix; + spin_lock_irqsave(&g_eventlock, irqstate); + ix = (g_evt_in + 1) & (OZ_MAX_EVTS - 1); + if (ix != g_evt_out) { + struct oz_event *e = &g_events[g_evt_in]; + e->jiffies = jiffies; + e->evt = evt; + e->ctx1 = ctx1; + e->ctx2 = ctx2; + e->ctx3 = ctx3; + e->ctx4 = ctx4; + g_evt_in = ix; + } else { + g_missed_events++; + } + spin_unlock_irqrestore(&g_eventlock, irqstate); +} +/*------------------------------------------------------------------------------ + * Context: process + */ +int oz_events_copy(struct oz_evtlist __user *lst) +{ + int first; + int ix; + struct hdr { + int count; + int missed; + } hdr; + ix = g_evt_out; + hdr.count = g_evt_in - ix; + if (hdr.count < 0) + hdr.count += OZ_MAX_EVTS; + if (hdr.count > OZ_EVT_LIST_SZ) + hdr.count = OZ_EVT_LIST_SZ; + hdr.missed = g_missed_events; + g_missed_events = 0; + if (copy_to_user((void __user *)lst, &hdr, sizeof(hdr))) + return -EFAULT; + first = OZ_MAX_EVTS - ix; + if (first > hdr.count) + first = hdr.count; + if (first) { + int sz = first*sizeof(struct oz_event); + void __user *p = (void __user *)lst->evts; + if (copy_to_user(p, &g_events[ix], sz)) + return -EFAULT; + if (hdr.count > first) { + p = (void __user *)&lst->evts[first]; + sz = (hdr.count-first)*sizeof(struct oz_event); + if (copy_to_user(p, g_events, sz)) + return -EFAULT; + } + } + ix += hdr.count; + if (ix >= OZ_MAX_EVTS) + ix -= OZ_MAX_EVTS; + g_evt_out = ix; + return 0; +} +/*------------------------------------------------------------------------------ + * Context: process + */ +void oz_events_clear(void) +{ + unsigned long irqstate; + spin_lock_irqsave(&g_eventlock, irqstate); + g_evt_in = g_evt_out = 0; + g_missed_events = 0; + spin_unlock_irqrestore(&g_eventlock, irqstate); +} +#endif /* WANT_EVENT_TRACE */ + diff --git a/drivers/staging/ozwpan/ozevent.h b/drivers/staging/ozwpan/ozevent.h new file mode 100644 index 0000000..f033d01 --- /dev/null +++ b/drivers/staging/ozwpan/ozevent.h @@ -0,0 +1,31 @@ +/* ----------------------------------------------------------------------------- + * Copyright (c) 2011 Ozmo Inc + * Released under the GNU General Public License Version 2 (GPLv2). + * ----------------------------------------------------------------------------- + */ +#ifndef _OZEVENT_H +#define _OZEVENT_H +#include "ozconfig.h" +#include "ozeventdef.h" + +#ifdef WANT_EVENT_TRACE +extern unsigned long g_evt_mask; +void oz_event_init(void); +void oz_event_term(void); +void oz_event_log2(u8 evt, u8 ctx1, u16 ctx2, void *ctx3, unsigned ctx4); +#define oz_event_log(__evt, __ctx1, __ctx2, __ctx3, __ctx4) \ + do { \ + if ((1<<(__evt)) & g_evt_mask) \ + oz_event_log2(__evt, __ctx1, __ctx2, __ctx3, __ctx4); \ + } while (0) +int oz_events_copy(struct oz_evtlist __user *lst); +void oz_events_clear(void); +#else +#define oz_event_init() +#define oz_event_term() +#define oz_event_log(__evt, __ctx1, __ctx2, __ctx3, __ctx4) +#define oz_events_copy(__lst) +#define oz_events_clear() +#endif /* WANT_EVENT_TRACE */ + +#endif /* _OZEVENT_H */ diff --git a/drivers/staging/ozwpan/ozurbparanoia.c b/drivers/staging/ozwpan/ozurbparanoia.c new file mode 100644 index 0000000..55b9afb --- /dev/null +++ b/drivers/staging/ozwpan/ozurbparanoia.c @@ -0,0 +1,53 @@ +/* ----------------------------------------------------------------------------- + * Copyright (c) 2011 Ozmo Inc + * Released under the GNU General Public License Version 2 (GPLv2). + * ----------------------------------------------------------------------------- + */ +#include <linux/usb.h> +#include "ozconfig.h" +#ifdef WANT_URB_PARANOIA +#include "ozurbparanoia.h" +#include "oztrace.h" +/*----------------------------------------------------------------------------- + */ +#define OZ_MAX_URBS 1000 +struct urb *g_urb_memory[OZ_MAX_URBS]; +int g_nb_urbs; +DEFINE_SPINLOCK(g_urb_mem_lock); +/*----------------------------------------------------------------------------- + */ +void oz_remember_urb(struct urb *urb) +{ + unsigned long irq_state; + spin_lock_irqsave(&g_urb_mem_lock, irq_state); + if (g_nb_urbs < OZ_MAX_URBS) { + g_urb_memory[g_nb_urbs++] = urb; + oz_trace("%lu: urb up = %d %p\n", jiffies, g_nb_urbs, urb); + } else { + oz_trace("ERROR urb buffer full\n"); + } + spin_unlock_irqrestore(&g_urb_mem_lock, irq_state); +} +/*------------------------------------------------------------------------------ + */ +int oz_forget_urb(struct urb *urb) +{ + unsigned long irq_state; + int i; + int rc = -1; + spin_lock_irqsave(&g_urb_mem_lock, irq_state); + for (i = 0; i < g_nb_urbs; i++) { + if (g_urb_memory[i] == urb) { + rc = 0; + if (--g_nb_urbs > i) + memcpy(&g_urb_memory[i], &g_urb_memory[i+1], + (g_nb_urbs - i) * sizeof(struct urb *)); + oz_trace("%lu: urb down = %d %p\n", + jiffies, g_nb_urbs, urb); + } + } + spin_unlock_irqrestore(&g_urb_mem_lock, irq_state); + return rc; +} +#endif /* #ifdef WANT_URB_PARANOIA */ + diff --git a/drivers/staging/ozwpan/ozurbparanoia.h b/drivers/staging/ozwpan/ozurbparanoia.h new file mode 100644 index 0000000..00f5a3a --- /dev/null +++ b/drivers/staging/ozwpan/ozurbparanoia.h @@ -0,0 +1,19 @@ +#ifndef _OZURBPARANOIA_H +#define _OZURBPARANOIA_H +/* ----------------------------------------------------------------------------- + * Released under the GNU General Public License Version 2 (GPLv2). + * Copyright (c) 2011 Ozmo Inc + * ----------------------------------------------------------------------------- + */ + +#ifdef WANT_URB_PARANOIA +void oz_remember_urb(struct urb *urb); +int oz_forget_urb(struct urb *urb); +#else +#define oz_remember_urb(__x) +#define oz_forget_urb(__x) 0 +#endif /* WANT_URB_PARANOIA */ + + +#endif /* _OZURBPARANOIA_H */ + -- 1.7.7.6 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html