blob: cfc0f3996b4c19d842e3b51fce18bb4debc9b32f [file] [log] [blame]
Chris Kellybc3157d2012-02-20 21:11:37 +00001/* -----------------------------------------------------------------------------
2 * Copyright (c) 2011 Ozmo Inc
3 * Released under the GNU General Public License Version 2 (GPLv2).
4 * -----------------------------------------------------------------------------
5 */
6#include <linux/init.h>
7#include <linux/module.h>
8#include <linux/netdevice.h>
Joe Perchesf724b582013-07-23 13:45:00 +01009#include "ozdbg.h"
Chris Kellybc3157d2012-02-20 21:11:37 +000010#include "ozprotocol.h"
11#include "ozeltbuf.h"
12#include "ozpd.h"
Joe Perchesf724b582013-07-23 13:45:00 +010013
Chris Kellybc3157d2012-02-20 21:11:37 +000014/*------------------------------------------------------------------------------
15 */
16#define OZ_ELT_INFO_MAGIC_USED 0x35791057
17#define OZ_ELT_INFO_MAGIC_FREE 0x78940102
18/*------------------------------------------------------------------------------
19 * Context: softirq-serialized
20 */
21int oz_elt_buf_init(struct oz_elt_buf *buf)
22{
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);
29 return 0;
30}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010031
Chris Kellybc3157d2012-02-20 21:11:37 +000032/*------------------------------------------------------------------------------
33 * Context: softirq or process
34 */
35void oz_elt_buf_term(struct oz_elt_buf *buf)
36{
37 struct list_head *e;
38 int i;
Rupesh Gujare18f81912013-08-13 18:24:21 +010039
Chris Kellybc3157d2012-02-20 21:11:37 +000040 /* Free any elements in the order or isoc lists. */
41 for (i = 0; i < 2; i++) {
42 struct list_head *list;
43 if (i)
44 list = &buf->order_list;
45 else
46 list = &buf->isoc_list;
47 e = list->next;
48 while (e != list) {
49 struct oz_elt_info *ei =
50 container_of(e, struct oz_elt_info, link_order);
51 e = e->next;
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -080052 kfree(ei);
Chris Kellybc3157d2012-02-20 21:11:37 +000053 }
54 }
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;
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -080060 kfree(ei);
Chris Kellybc3157d2012-02-20 21:11:37 +000061 }
62 buf->free_elts = 0;
63}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010064
Chris Kellybc3157d2012-02-20 21:11:37 +000065/*------------------------------------------------------------------------------
66 * Context: softirq or process
67 */
68struct oz_elt_info *oz_elt_info_alloc(struct oz_elt_buf *buf)
69{
Rupesh Gujarea15e0422013-08-13 18:29:24 +010070 struct oz_elt_info *ei;
Rupesh Gujare18f81912013-08-13 18:24:21 +010071
Chris Kellybc3157d2012-02-20 21:11:37 +000072 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;
76 buf->free_elts--;
77 spin_unlock_bh(&buf->lock);
78 if (ei->magic != OZ_ELT_INFO_MAGIC_FREE) {
Joe Perchesf724b582013-07-23 13:45:00 +010079 oz_dbg(ON, "%s: ei with bad magic: 0x%x\n",
80 __func__, ei->magic);
Chris Kellybc3157d2012-02-20 21:11:37 +000081 }
82 } else {
83 spin_unlock_bh(&buf->lock);
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -080084 ei = kmalloc(sizeof(struct oz_elt_info), GFP_ATOMIC);
Chris Kellybc3157d2012-02-20 21:11:37 +000085 }
86 if (ei) {
87 ei->flags = 0;
88 ei->app_id = 0;
Peter Huewe953b1902013-02-15 15:22:24 +010089 ei->callback = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +000090 ei->context = 0;
Peter Huewe953b1902013-02-15 15:22:24 +010091 ei->stream = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +000092 ei->magic = OZ_ELT_INFO_MAGIC_USED;
93 INIT_LIST_HEAD(&ei->link);
94 INIT_LIST_HEAD(&ei->link_order);
95 }
96 return ei;
97}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010098
Chris Kellybc3157d2012-02-20 21:11:37 +000099/*------------------------------------------------------------------------------
100 * Precondition: oz_elt_buf.lock must be held.
101 * Context: softirq or process
102 */
103void oz_elt_info_free(struct oz_elt_buf *buf, struct oz_elt_info *ei)
104{
105 if (ei) {
106 if (ei->magic == OZ_ELT_INFO_MAGIC_USED) {
107 buf->free_elts++;
108 ei->link.next = buf->elt_pool;
109 buf->elt_pool = &ei->link;
110 ei->magic = OZ_ELT_INFO_MAGIC_FREE;
111 } else {
Joe Perchesf724b582013-07-23 13:45:00 +0100112 oz_dbg(ON, "%s: bad magic ei: %p magic: 0x%x\n",
113 __func__, ei, ei->magic);
Chris Kellybc3157d2012-02-20 21:11:37 +0000114 }
115 }
116}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100117
Chris Kellybc3157d2012-02-20 21:11:37 +0000118/*------------------------------------------------------------------------------
119 * Context: softirq
120 */
121void oz_elt_info_free_chain(struct oz_elt_buf *buf, struct list_head *list)
122{
123 struct list_head *e;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100124
Chris Kellybc3157d2012-02-20 21:11:37 +0000125 e = list->next;
126 spin_lock_bh(&buf->lock);
127 while (e != list) {
128 struct oz_elt_info *ei;
129 ei = container_of(e, struct oz_elt_info, link);
130 e = e->next;
131 oz_elt_info_free(buf, ei);
132 }
133 spin_unlock_bh(&buf->lock);
134}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100135
Chris Kellybc3157d2012-02-20 21:11:37 +0000136/*------------------------------------------------------------------------------
137 */
138int oz_elt_stream_create(struct oz_elt_buf *buf, u8 id, int max_buf_count)
139{
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800140 struct oz_elt_stream *st;
141
Joe Perchesf724b582013-07-23 13:45:00 +0100142 oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800143
144 st = kzalloc(sizeof(struct oz_elt_stream), GFP_ATOMIC | __GFP_ZERO);
Peter Huewe953b1902013-02-15 15:22:24 +0100145 if (st == NULL)
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800146 return -ENOMEM;
Chris Kellybc3157d2012-02-20 21:11:37 +0000147 atomic_set(&st->ref_count, 1);
148 st->id = id;
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);
154 return 0;
155}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100156
Chris Kellybc3157d2012-02-20 21:11:37 +0000157/*------------------------------------------------------------------------------
158 */
159int oz_elt_stream_delete(struct oz_elt_buf *buf, u8 id)
160{
161 struct list_head *e;
Peter Huewe953b1902013-02-15 15:22:24 +0100162 struct oz_elt_stream *st = NULL;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100163
Joe Perchesf724b582013-07-23 13:45:00 +0100164 oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
Chris Kellybc3157d2012-02-20 21:11:37 +0000165 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);
169 if (st->id == id) {
170 list_del(e);
171 break;
172 }
Peter Huewe953b1902013-02-15 15:22:24 +0100173 st = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +0000174 }
175 if (!st) {
176 spin_unlock_bh(&buf->lock);
177 return -1;
178 }
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);
183 e = e->next;
184 list_del_init(&ei->link);
185 list_del_init(&ei->link_order);
186 st->buf_count -= ei->length;
Joe Perchesf724b582013-07-23 13:45:00 +0100187 oz_dbg(STREAM, "Stream down: %d %d %d\n",
188 st->buf_count, ei->length, atomic_read(&st->ref_count));
Chris Kellybc3157d2012-02-20 21:11:37 +0000189 oz_elt_stream_put(st);
190 oz_elt_info_free(buf, ei);
191 }
192 spin_unlock_bh(&buf->lock);
193 oz_elt_stream_put(st);
194 return 0;
195}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100196
Chris Kellybc3157d2012-02-20 21:11:37 +0000197/*------------------------------------------------------------------------------
198 */
199void oz_elt_stream_get(struct oz_elt_stream *st)
200{
201 atomic_inc(&st->ref_count);
202}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100203
Chris Kellybc3157d2012-02-20 21:11:37 +0000204/*------------------------------------------------------------------------------
205 */
206void oz_elt_stream_put(struct oz_elt_stream *st)
207{
208 if (atomic_dec_and_test(&st->ref_count)) {
Joe Perchesf724b582013-07-23 13:45:00 +0100209 oz_dbg(ON, "Stream destroyed\n");
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800210 kfree(st);
Chris Kellybc3157d2012-02-20 21:11:37 +0000211 }
212}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100213
Chris Kellybc3157d2012-02-20 21:11:37 +0000214/*------------------------------------------------------------------------------
215 * Precondition: Element buffer lock must be held.
216 * If this function fails the caller is responsible for deallocating the elt
217 * info structure.
218 */
219int oz_queue_elt_info(struct oz_elt_buf *buf, u8 isoc, u8 id,
220 struct oz_elt_info *ei)
221{
Peter Huewe953b1902013-02-15 15:22:24 +0100222 struct oz_elt_stream *st = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +0000223 struct list_head *e;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100224
Chris Kellybc3157d2012-02-20 21:11:37 +0000225 if (id) {
226 list_for_each(e, &buf->stream_list) {
227 st = container_of(e, struct oz_elt_stream, link);
228 if (st->id == id)
229 break;
230 }
231 if (e == &buf->stream_list) {
232 /* Stream specified but stream not known so fail.
233 * Caller deallocates element info. */
234 return -1;
235 }
236 }
237 if (st) {
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
240 * this field.
241 */
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;
250 }
251 /* Claim stream and update accounts */
252 oz_elt_stream_get(st);
253 ei->stream = st;
254 st->buf_count += ei->length;
255 /* Add to list in stream. */
256 list_add_tail(&ei->link, &st->elt_list);
Joe Perchesf724b582013-07-23 13:45:00 +0100257 oz_dbg(STREAM, "Stream up: %d %d\n", st->buf_count, ei->length);
Chris Kellybc3157d2012-02-20 21:11:37 +0000258 /* Check if we have too much buffered for this stream. If so
259 * start dropping elements until we are back in bounds.
260 */
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);
271 }
272 }
273 list_add_tail(&ei->link_order, isoc ?
274 &buf->isoc_list : &buf->order_list);
275 return 0;
276}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100277
Chris Kellybc3157d2012-02-20 21:11:37 +0000278/*------------------------------------------------------------------------------
279 */
280int oz_select_elts_for_tx(struct oz_elt_buf *buf, u8 isoc, unsigned *len,
281 unsigned max_len, struct list_head *list)
282{
283 int count = 0;
284 struct list_head *e;
285 struct list_head *el;
286 struct oz_elt_info *ei;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100287
Chris Kellybc3157d2012-02-20 21:11:37 +0000288 spin_lock_bh(&buf->lock);
289 if (isoc)
290 el = &buf->isoc_list;
291 else
292 el = &buf->order_list;
293 e = el->next;
294 while (e != el) {
295 struct oz_app_hdr *app_hdr;
296 ei = container_of(e, struct oz_elt_info, link_order);
297 e = e->next;
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;
304 *len += ei->length;
305 list_del(&ei->link);
306 list_del(&ei->link_order);
307 if (ei->stream) {
308 ei->stream->buf_count -= ei->length;
Joe Perchesf724b582013-07-23 13:45:00 +0100309 oz_dbg(STREAM, "Stream down: %d %d\n",
310 ei->stream->buf_count, ei->length);
Chris Kellybc3157d2012-02-20 21:11:37 +0000311 oz_elt_stream_put(ei->stream);
Peter Huewe953b1902013-02-15 15:22:24 +0100312 ei->stream = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +0000313 }
314 INIT_LIST_HEAD(&ei->link_order);
315 list_add_tail(&ei->link, list);
316 count++;
317 } else {
318 break;
319 }
320 }
321 spin_unlock_bh(&buf->lock);
322 return count;
323}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100324
Chris Kellybc3157d2012-02-20 21:11:37 +0000325/*------------------------------------------------------------------------------
326 */
327int oz_are_elts_available(struct oz_elt_buf *buf)
328{
329 return buf->order_list.next != &buf->order_list;
330}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100331
Chris Kellybc3157d2012-02-20 21:11:37 +0000332/*------------------------------------------------------------------------------
333 */
334void oz_trim_elt_pool(struct oz_elt_buf *buf)
335{
Peter Huewe953b1902013-02-15 15:22:24 +0100336 struct list_head *free = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +0000337 struct list_head *e;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100338
Chris Kellybc3157d2012-02-20 21:11:37 +0000339 spin_lock_bh(&buf->lock);
340 while (buf->free_elts > buf->max_free_elts) {
341 e = buf->elt_pool;
342 buf->elt_pool = e->next;
343 e->next = free;
344 free = e;
345 buf->free_elts--;
346 }
347 spin_unlock_bh(&buf->lock);
348 while (free) {
349 struct oz_elt_info *ei =
350 container_of(free, struct oz_elt_info, link);
351 free = free->next;
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800352 kfree(ei);
Chris Kellybc3157d2012-02-20 21:11:37 +0000353 }
354}