1 /* -----------------------------------------------------------------------------
2 * Copyright (c) 2011 Ozmo Inc
3 * Released under the GNU General Public License Version 2 (GPLv2).
4 * -----------------------------------------------------------------------------
6 #include <linux/init.h>
7 #include <linux/module.h>
8 #include <linux/netdevice.h>
10 #include "ozprotocol.h"
14 /*------------------------------------------------------------------------------
16 #define OZ_ELT_INFO_MAGIC_USED 0x35791057
17 #define OZ_ELT_INFO_MAGIC_FREE 0x78940102
18 /*------------------------------------------------------------------------------
19 * Context: softirq-serialized
21 int oz_elt_buf_init(struct oz_elt_buf *buf)
23 memset(buf, 0, sizeof(struct oz_elt_buf));
24 INIT_LIST_HEAD(&buf->stream_list);
25 INIT_LIST_HEAD(&buf->order_list);
26 INIT_LIST_HEAD(&buf->isoc_list);
27 buf->max_free_elts = 32;
28 spin_lock_init(&buf->lock);
32 /*------------------------------------------------------------------------------
33 * Context: softirq or process
35 void oz_elt_buf_term(struct oz_elt_buf *buf)
40 /* Free any elements in the order or isoc lists. */
41 for (i = 0; i < 2; i++) {
42 struct list_head *list;
44 list = &buf->order_list;
46 list = &buf->isoc_list;
49 struct oz_elt_info *ei =
50 container_of(e, struct oz_elt_info, link_order);
55 /* Free any elelment in the pool. */
56 while (buf->elt_pool) {
57 struct oz_elt_info *ei =
58 container_of(buf->elt_pool, struct oz_elt_info, link);
59 buf->elt_pool = buf->elt_pool->next;
65 /*------------------------------------------------------------------------------
66 * Context: softirq or process
68 struct oz_elt_info *oz_elt_info_alloc(struct oz_elt_buf *buf)
70 struct oz_elt_info *ei;
72 spin_lock_bh(&buf->lock);
73 if (buf->free_elts && buf->elt_pool) {
74 ei = container_of(buf->elt_pool, struct oz_elt_info, link);
75 buf->elt_pool = ei->link.next;
77 spin_unlock_bh(&buf->lock);
78 if (ei->magic != OZ_ELT_INFO_MAGIC_FREE) {
79 oz_dbg(ON, "%s: ei with bad magic: 0x%x\n",
83 spin_unlock_bh(&buf->lock);
84 ei = kmalloc(sizeof(struct oz_elt_info), GFP_ATOMIC);
92 ei->magic = OZ_ELT_INFO_MAGIC_USED;
93 INIT_LIST_HEAD(&ei->link);
94 INIT_LIST_HEAD(&ei->link_order);
99 /*------------------------------------------------------------------------------
100 * Precondition: oz_elt_buf.lock must be held.
101 * Context: softirq or process
103 void oz_elt_info_free(struct oz_elt_buf *buf, struct oz_elt_info *ei)
106 if (ei->magic == OZ_ELT_INFO_MAGIC_USED) {
108 ei->link.next = buf->elt_pool;
109 buf->elt_pool = &ei->link;
110 ei->magic = OZ_ELT_INFO_MAGIC_FREE;
112 oz_dbg(ON, "%s: bad magic ei: %p magic: 0x%x\n",
113 __func__, ei, ei->magic);
118 /*------------------------------------------------------------------------------
121 void oz_elt_info_free_chain(struct oz_elt_buf *buf, struct list_head *list)
126 spin_lock_bh(&buf->lock);
128 struct oz_elt_info *ei;
129 ei = container_of(e, struct oz_elt_info, link);
131 oz_elt_info_free(buf, ei);
133 spin_unlock_bh(&buf->lock);
136 /*------------------------------------------------------------------------------
138 int oz_elt_stream_create(struct oz_elt_buf *buf, u8 id, int max_buf_count)
140 struct oz_elt_stream *st;
142 oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
144 st = kzalloc(sizeof(struct oz_elt_stream), GFP_ATOMIC | __GFP_ZERO);
147 atomic_set(&st->ref_count, 1);
149 st->max_buf_count = max_buf_count;
150 INIT_LIST_HEAD(&st->elt_list);
151 spin_lock_bh(&buf->lock);
152 list_add_tail(&st->link, &buf->stream_list);
153 spin_unlock_bh(&buf->lock);
157 /*------------------------------------------------------------------------------
159 int oz_elt_stream_delete(struct oz_elt_buf *buf, u8 id)
162 struct oz_elt_stream *st = NULL;
164 oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
165 spin_lock_bh(&buf->lock);
166 e = buf->stream_list.next;
167 while (e != &buf->stream_list) {
168 st = container_of(e, struct oz_elt_stream, link);
176 spin_unlock_bh(&buf->lock);
179 e = st->elt_list.next;
180 while (e != &st->elt_list) {
181 struct oz_elt_info *ei =
182 container_of(e, struct oz_elt_info, link);
184 list_del_init(&ei->link);
185 list_del_init(&ei->link_order);
186 st->buf_count -= ei->length;
187 oz_dbg(STREAM, "Stream down: %d %d %d\n",
188 st->buf_count, ei->length, atomic_read(&st->ref_count));
189 oz_elt_stream_put(st);
190 oz_elt_info_free(buf, ei);
192 spin_unlock_bh(&buf->lock);
193 oz_elt_stream_put(st);
197 /*------------------------------------------------------------------------------
199 void oz_elt_stream_get(struct oz_elt_stream *st)
201 atomic_inc(&st->ref_count);
204 /*------------------------------------------------------------------------------
206 void oz_elt_stream_put(struct oz_elt_stream *st)
208 if (atomic_dec_and_test(&st->ref_count)) {
209 oz_dbg(ON, "Stream destroyed\n");
214 /*------------------------------------------------------------------------------
215 * Precondition: Element buffer lock must be held.
216 * If this function fails the caller is responsible for deallocating the elt
219 int oz_queue_elt_info(struct oz_elt_buf *buf, u8 isoc, u8 id,
220 struct oz_elt_info *ei)
222 struct oz_elt_stream *st = NULL;
226 list_for_each(e, &buf->stream_list) {
227 st = container_of(e, struct oz_elt_stream, link);
231 if (e == &buf->stream_list) {
232 /* Stream specified but stream not known so fail.
233 * Caller deallocates element info. */
238 /* If this is an ISOC fixed element that needs a frame number
239 * then insert that now. Earlier we stored the unit count in
242 struct oz_isoc_fixed *body = (struct oz_isoc_fixed *)
243 &ei->data[sizeof(struct oz_elt)];
244 if ((body->app_id == OZ_APPID_USB) && (body->type
245 == OZ_USB_ENDPOINT_DATA) &&
246 (body->format == OZ_DATA_F_ISOC_FIXED)) {
247 u8 unit_count = body->frame_number;
248 body->frame_number = st->frame_number;
249 st->frame_number += unit_count;
251 /* Claim stream and update accounts */
252 oz_elt_stream_get(st);
254 st->buf_count += ei->length;
255 /* Add to list in stream. */
256 list_add_tail(&ei->link, &st->elt_list);
257 oz_dbg(STREAM, "Stream up: %d %d\n", st->buf_count, ei->length);
258 /* Check if we have too much buffered for this stream. If so
259 * start dropping elements until we are back in bounds.
261 while ((st->buf_count > st->max_buf_count) &&
262 !list_empty(&st->elt_list)) {
263 struct oz_elt_info *ei2 =
264 list_first_entry(&st->elt_list,
265 struct oz_elt_info, link);
266 list_del_init(&ei2->link);
267 list_del_init(&ei2->link_order);
268 st->buf_count -= ei2->length;
269 oz_elt_info_free(buf, ei2);
270 oz_elt_stream_put(st);
273 list_add_tail(&ei->link_order, isoc ?
274 &buf->isoc_list : &buf->order_list);
278 /*------------------------------------------------------------------------------
280 int oz_select_elts_for_tx(struct oz_elt_buf *buf, u8 isoc, unsigned *len,
281 unsigned max_len, struct list_head *list)
285 struct list_head *el;
286 struct oz_elt_info *ei;
288 spin_lock_bh(&buf->lock);
290 el = &buf->isoc_list;
292 el = &buf->order_list;
295 struct oz_app_hdr *app_hdr;
296 ei = container_of(e, struct oz_elt_info, link_order);
298 if ((*len + ei->length) <= max_len) {
299 app_hdr = (struct oz_app_hdr *)
300 &ei->data[sizeof(struct oz_elt)];
301 app_hdr->elt_seq_num = buf->tx_seq_num[ei->app_id]++;
302 if (buf->tx_seq_num[ei->app_id] == 0)
303 buf->tx_seq_num[ei->app_id] = 1;
306 list_del(&ei->link_order);
308 ei->stream->buf_count -= ei->length;
309 oz_dbg(STREAM, "Stream down: %d %d\n",
310 ei->stream->buf_count, ei->length);
311 oz_elt_stream_put(ei->stream);
314 INIT_LIST_HEAD(&ei->link_order);
315 list_add_tail(&ei->link, list);
321 spin_unlock_bh(&buf->lock);
325 /*------------------------------------------------------------------------------
327 int oz_are_elts_available(struct oz_elt_buf *buf)
329 return buf->order_list.next != &buf->order_list;
332 /*------------------------------------------------------------------------------
334 void oz_trim_elt_pool(struct oz_elt_buf *buf)
336 struct list_head *free = NULL;
339 spin_lock_bh(&buf->lock);
340 while (buf->free_elts > buf->max_free_elts) {
342 buf->elt_pool = e->next;
347 spin_unlock_bh(&buf->lock);
349 struct oz_elt_info *ei =
350 container_of(free, struct oz_elt_info, link);