blob: 9b86486c6b114b57fbd3792800b473fb47f445b8 [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#define OZ_ELT_INFO_MAGIC_USED 0x35791057
15#define OZ_ELT_INFO_MAGIC_FREE 0x78940102
Rupesh Gujare4e7fb822013-08-23 16:11:02 +010016
17/*
Chris Kellybc3157d2012-02-20 21:11:37 +000018 * Context: softirq-serialized
19 */
20int oz_elt_buf_init(struct oz_elt_buf *buf)
21{
22 memset(buf, 0, sizeof(struct oz_elt_buf));
23 INIT_LIST_HEAD(&buf->stream_list);
24 INIT_LIST_HEAD(&buf->order_list);
25 INIT_LIST_HEAD(&buf->isoc_list);
26 buf->max_free_elts = 32;
27 spin_lock_init(&buf->lock);
28 return 0;
29}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010030
Rupesh Gujare4e7fb822013-08-23 16:11:02 +010031/*
Chris Kellybc3157d2012-02-20 21:11:37 +000032 * Context: softirq or process
33 */
34void oz_elt_buf_term(struct oz_elt_buf *buf)
35{
36 struct list_head *e;
37 int i;
Rupesh Gujare18f81912013-08-13 18:24:21 +010038
Chris Kellybc3157d2012-02-20 21:11:37 +000039 /* Free any elements in the order or isoc lists. */
40 for (i = 0; i < 2; i++) {
41 struct list_head *list;
42 if (i)
43 list = &buf->order_list;
44 else
45 list = &buf->isoc_list;
46 e = list->next;
47 while (e != list) {
48 struct oz_elt_info *ei =
49 container_of(e, struct oz_elt_info, link_order);
50 e = e->next;
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -080051 kfree(ei);
Chris Kellybc3157d2012-02-20 21:11:37 +000052 }
53 }
54 /* Free any elelment in the pool. */
55 while (buf->elt_pool) {
56 struct oz_elt_info *ei =
57 container_of(buf->elt_pool, struct oz_elt_info, link);
58 buf->elt_pool = buf->elt_pool->next;
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -080059 kfree(ei);
Chris Kellybc3157d2012-02-20 21:11:37 +000060 }
61 buf->free_elts = 0;
62}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010063
Rupesh Gujare4e7fb822013-08-23 16:11:02 +010064/*
Chris Kellybc3157d2012-02-20 21:11:37 +000065 * Context: softirq or process
66 */
67struct oz_elt_info *oz_elt_info_alloc(struct oz_elt_buf *buf)
68{
Rupesh Gujarea15e0422013-08-13 18:29:24 +010069 struct oz_elt_info *ei;
Rupesh Gujare18f81912013-08-13 18:24:21 +010070
Chris Kellybc3157d2012-02-20 21:11:37 +000071 spin_lock_bh(&buf->lock);
72 if (buf->free_elts && buf->elt_pool) {
73 ei = container_of(buf->elt_pool, struct oz_elt_info, link);
74 buf->elt_pool = ei->link.next;
75 buf->free_elts--;
76 spin_unlock_bh(&buf->lock);
77 if (ei->magic != OZ_ELT_INFO_MAGIC_FREE) {
Joe Perchesf724b582013-07-23 13:45:00 +010078 oz_dbg(ON, "%s: ei with bad magic: 0x%x\n",
79 __func__, ei->magic);
Chris Kellybc3157d2012-02-20 21:11:37 +000080 }
81 } else {
82 spin_unlock_bh(&buf->lock);
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -080083 ei = kmalloc(sizeof(struct oz_elt_info), GFP_ATOMIC);
Chris Kellybc3157d2012-02-20 21:11:37 +000084 }
85 if (ei) {
86 ei->flags = 0;
87 ei->app_id = 0;
Peter Huewe953b1902013-02-15 15:22:24 +010088 ei->callback = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +000089 ei->context = 0;
Peter Huewe953b1902013-02-15 15:22:24 +010090 ei->stream = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +000091 ei->magic = OZ_ELT_INFO_MAGIC_USED;
92 INIT_LIST_HEAD(&ei->link);
93 INIT_LIST_HEAD(&ei->link_order);
94 }
95 return ei;
96}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010097
Rupesh Gujare4e7fb822013-08-23 16:11:02 +010098/*
Chris Kellybc3157d2012-02-20 21:11:37 +000099 * Precondition: oz_elt_buf.lock must be held.
100 * Context: softirq or process
101 */
102void oz_elt_info_free(struct oz_elt_buf *buf, struct oz_elt_info *ei)
103{
104 if (ei) {
105 if (ei->magic == OZ_ELT_INFO_MAGIC_USED) {
106 buf->free_elts++;
107 ei->link.next = buf->elt_pool;
108 buf->elt_pool = &ei->link;
109 ei->magic = OZ_ELT_INFO_MAGIC_FREE;
110 } else {
Joe Perchesf724b582013-07-23 13:45:00 +0100111 oz_dbg(ON, "%s: bad magic ei: %p magic: 0x%x\n",
112 __func__, ei, ei->magic);
Chris Kellybc3157d2012-02-20 21:11:37 +0000113 }
114 }
115}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100116
Chris Kellybc3157d2012-02-20 21:11:37 +0000117/*------------------------------------------------------------------------------
118 * Context: softirq
119 */
120void oz_elt_info_free_chain(struct oz_elt_buf *buf, struct list_head *list)
121{
122 struct list_head *e;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100123
Chris Kellybc3157d2012-02-20 21:11:37 +0000124 e = list->next;
125 spin_lock_bh(&buf->lock);
126 while (e != list) {
127 struct oz_elt_info *ei;
128 ei = container_of(e, struct oz_elt_info, link);
129 e = e->next;
130 oz_elt_info_free(buf, ei);
131 }
132 spin_unlock_bh(&buf->lock);
133}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100134
Chris Kellybc3157d2012-02-20 21:11:37 +0000135int oz_elt_stream_create(struct oz_elt_buf *buf, u8 id, int max_buf_count)
136{
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800137 struct oz_elt_stream *st;
138
Joe Perchesf724b582013-07-23 13:45:00 +0100139 oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800140
141 st = kzalloc(sizeof(struct oz_elt_stream), GFP_ATOMIC | __GFP_ZERO);
Peter Huewe953b1902013-02-15 15:22:24 +0100142 if (st == NULL)
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800143 return -ENOMEM;
Chris Kellybc3157d2012-02-20 21:11:37 +0000144 atomic_set(&st->ref_count, 1);
145 st->id = id;
146 st->max_buf_count = max_buf_count;
147 INIT_LIST_HEAD(&st->elt_list);
148 spin_lock_bh(&buf->lock);
149 list_add_tail(&st->link, &buf->stream_list);
150 spin_unlock_bh(&buf->lock);
151 return 0;
152}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100153
Chris Kellybc3157d2012-02-20 21:11:37 +0000154int oz_elt_stream_delete(struct oz_elt_buf *buf, u8 id)
155{
156 struct list_head *e;
Peter Huewe953b1902013-02-15 15:22:24 +0100157 struct oz_elt_stream *st = NULL;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100158
Joe Perchesf724b582013-07-23 13:45:00 +0100159 oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
Chris Kellybc3157d2012-02-20 21:11:37 +0000160 spin_lock_bh(&buf->lock);
161 e = buf->stream_list.next;
162 while (e != &buf->stream_list) {
163 st = container_of(e, struct oz_elt_stream, link);
164 if (st->id == id) {
165 list_del(e);
166 break;
167 }
Peter Huewe953b1902013-02-15 15:22:24 +0100168 st = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +0000169 }
170 if (!st) {
171 spin_unlock_bh(&buf->lock);
172 return -1;
173 }
174 e = st->elt_list.next;
175 while (e != &st->elt_list) {
176 struct oz_elt_info *ei =
177 container_of(e, struct oz_elt_info, link);
178 e = e->next;
179 list_del_init(&ei->link);
180 list_del_init(&ei->link_order);
181 st->buf_count -= ei->length;
Joe Perchesf724b582013-07-23 13:45:00 +0100182 oz_dbg(STREAM, "Stream down: %d %d %d\n",
183 st->buf_count, ei->length, atomic_read(&st->ref_count));
Chris Kellybc3157d2012-02-20 21:11:37 +0000184 oz_elt_stream_put(st);
185 oz_elt_info_free(buf, ei);
186 }
187 spin_unlock_bh(&buf->lock);
188 oz_elt_stream_put(st);
189 return 0;
190}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100191
Chris Kellybc3157d2012-02-20 21:11:37 +0000192void oz_elt_stream_get(struct oz_elt_stream *st)
193{
194 atomic_inc(&st->ref_count);
195}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100196
Chris Kellybc3157d2012-02-20 21:11:37 +0000197void oz_elt_stream_put(struct oz_elt_stream *st)
198{
199 if (atomic_dec_and_test(&st->ref_count)) {
Joe Perchesf724b582013-07-23 13:45:00 +0100200 oz_dbg(ON, "Stream destroyed\n");
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800201 kfree(st);
Chris Kellybc3157d2012-02-20 21:11:37 +0000202 }
203}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100204
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100205/*
Chris Kellybc3157d2012-02-20 21:11:37 +0000206 * Precondition: Element buffer lock must be held.
207 * If this function fails the caller is responsible for deallocating the elt
208 * info structure.
209 */
210int oz_queue_elt_info(struct oz_elt_buf *buf, u8 isoc, u8 id,
211 struct oz_elt_info *ei)
212{
Peter Huewe953b1902013-02-15 15:22:24 +0100213 struct oz_elt_stream *st = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +0000214 struct list_head *e;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100215
Chris Kellybc3157d2012-02-20 21:11:37 +0000216 if (id) {
217 list_for_each(e, &buf->stream_list) {
218 st = container_of(e, struct oz_elt_stream, link);
219 if (st->id == id)
220 break;
221 }
222 if (e == &buf->stream_list) {
223 /* Stream specified but stream not known so fail.
224 * Caller deallocates element info. */
225 return -1;
226 }
227 }
228 if (st) {
229 /* If this is an ISOC fixed element that needs a frame number
230 * then insert that now. Earlier we stored the unit count in
231 * this field.
232 */
233 struct oz_isoc_fixed *body = (struct oz_isoc_fixed *)
234 &ei->data[sizeof(struct oz_elt)];
235 if ((body->app_id == OZ_APPID_USB) && (body->type
236 == OZ_USB_ENDPOINT_DATA) &&
237 (body->format == OZ_DATA_F_ISOC_FIXED)) {
238 u8 unit_count = body->frame_number;
239 body->frame_number = st->frame_number;
240 st->frame_number += unit_count;
241 }
242 /* Claim stream and update accounts */
243 oz_elt_stream_get(st);
244 ei->stream = st;
245 st->buf_count += ei->length;
246 /* Add to list in stream. */
247 list_add_tail(&ei->link, &st->elt_list);
Joe Perchesf724b582013-07-23 13:45:00 +0100248 oz_dbg(STREAM, "Stream up: %d %d\n", st->buf_count, ei->length);
Chris Kellybc3157d2012-02-20 21:11:37 +0000249 /* Check if we have too much buffered for this stream. If so
250 * start dropping elements until we are back in bounds.
251 */
252 while ((st->buf_count > st->max_buf_count) &&
253 !list_empty(&st->elt_list)) {
254 struct oz_elt_info *ei2 =
255 list_first_entry(&st->elt_list,
256 struct oz_elt_info, link);
257 list_del_init(&ei2->link);
258 list_del_init(&ei2->link_order);
259 st->buf_count -= ei2->length;
260 oz_elt_info_free(buf, ei2);
261 oz_elt_stream_put(st);
262 }
263 }
264 list_add_tail(&ei->link_order, isoc ?
265 &buf->isoc_list : &buf->order_list);
266 return 0;
267}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100268
Chris Kellybc3157d2012-02-20 21:11:37 +0000269int oz_select_elts_for_tx(struct oz_elt_buf *buf, u8 isoc, unsigned *len,
270 unsigned max_len, struct list_head *list)
271{
272 int count = 0;
273 struct list_head *e;
274 struct list_head *el;
275 struct oz_elt_info *ei;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100276
Chris Kellybc3157d2012-02-20 21:11:37 +0000277 spin_lock_bh(&buf->lock);
278 if (isoc)
279 el = &buf->isoc_list;
280 else
281 el = &buf->order_list;
282 e = el->next;
283 while (e != el) {
284 struct oz_app_hdr *app_hdr;
285 ei = container_of(e, struct oz_elt_info, link_order);
286 e = e->next;
287 if ((*len + ei->length) <= max_len) {
288 app_hdr = (struct oz_app_hdr *)
289 &ei->data[sizeof(struct oz_elt)];
290 app_hdr->elt_seq_num = buf->tx_seq_num[ei->app_id]++;
291 if (buf->tx_seq_num[ei->app_id] == 0)
292 buf->tx_seq_num[ei->app_id] = 1;
293 *len += ei->length;
294 list_del(&ei->link);
295 list_del(&ei->link_order);
296 if (ei->stream) {
297 ei->stream->buf_count -= ei->length;
Joe Perchesf724b582013-07-23 13:45:00 +0100298 oz_dbg(STREAM, "Stream down: %d %d\n",
299 ei->stream->buf_count, ei->length);
Chris Kellybc3157d2012-02-20 21:11:37 +0000300 oz_elt_stream_put(ei->stream);
Peter Huewe953b1902013-02-15 15:22:24 +0100301 ei->stream = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +0000302 }
303 INIT_LIST_HEAD(&ei->link_order);
304 list_add_tail(&ei->link, list);
305 count++;
306 } else {
307 break;
308 }
309 }
310 spin_unlock_bh(&buf->lock);
311 return count;
312}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100313
Chris Kellybc3157d2012-02-20 21:11:37 +0000314int oz_are_elts_available(struct oz_elt_buf *buf)
315{
316 return buf->order_list.next != &buf->order_list;
317}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100318
Chris Kellybc3157d2012-02-20 21:11:37 +0000319void oz_trim_elt_pool(struct oz_elt_buf *buf)
320{
Peter Huewe953b1902013-02-15 15:22:24 +0100321 struct list_head *free = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +0000322 struct list_head *e;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100323
Chris Kellybc3157d2012-02-20 21:11:37 +0000324 spin_lock_bh(&buf->lock);
325 while (buf->free_elts > buf->max_free_elts) {
326 e = buf->elt_pool;
327 buf->elt_pool = e->next;
328 e->next = free;
329 free = e;
330 buf->free_elts--;
331 }
332 spin_unlock_bh(&buf->lock);
333 while (free) {
334 struct oz_elt_info *ei =
335 container_of(free, struct oz_elt_info, link);
336 free = free->next;
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800337 kfree(ei);
Chris Kellybc3157d2012-02-20 21:11:37 +0000338 }
339}