Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / ozwpan / ozeltbuf.c
1 /* -----------------------------------------------------------------------------
2  * Copyright (c) 2011 Ozmo Inc
3  * Released under the GNU General Public License Version 2 (GPLv2).
4  * -----------------------------------------------------------------------------
5  */
6 #include <linux/module.h>
7 #include <linux/netdevice.h>
8 #include "ozdbg.h"
9 #include "ozprotocol.h"
10 #include "ozeltbuf.h"
11 #include "ozpd.h"
12
13 /*
14  * Context: softirq-serialized
15  */
16 void oz_elt_buf_init(struct oz_elt_buf *buf)
17 {
18         memset(buf, 0, sizeof(struct oz_elt_buf));
19         INIT_LIST_HEAD(&buf->stream_list);
20         INIT_LIST_HEAD(&buf->order_list);
21         INIT_LIST_HEAD(&buf->isoc_list);
22         spin_lock_init(&buf->lock);
23 }
24
25 /*
26  * Context: softirq or process
27  */
28 void oz_elt_buf_term(struct oz_elt_buf *buf)
29 {
30         struct oz_elt_info *ei, *n;
31
32         list_for_each_entry_safe(ei, n, &buf->isoc_list, link_order)
33                 kfree(ei);
34         list_for_each_entry_safe(ei, n, &buf->order_list, link_order)
35                 kfree(ei);
36 }
37
38 /*
39  * Context: softirq or process
40  */
41 struct oz_elt_info *oz_elt_info_alloc(struct oz_elt_buf *buf)
42 {
43         struct oz_elt_info *ei;
44
45         ei = kmem_cache_zalloc(oz_elt_info_cache, GFP_ATOMIC);
46         if (ei) {
47                 INIT_LIST_HEAD(&ei->link);
48                 INIT_LIST_HEAD(&ei->link_order);
49         }
50         return ei;
51 }
52
53 /*
54  * Precondition: oz_elt_buf.lock must be held.
55  * Context: softirq or process
56  */
57 void oz_elt_info_free(struct oz_elt_buf *buf, struct oz_elt_info *ei)
58 {
59         if (ei)
60                 kmem_cache_free(oz_elt_info_cache, ei);
61 }
62
63 /*------------------------------------------------------------------------------
64  * Context: softirq
65  */
66 void oz_elt_info_free_chain(struct oz_elt_buf *buf, struct list_head *list)
67 {
68         struct oz_elt_info *ei, *n;
69
70         spin_lock_bh(&buf->lock);
71         list_for_each_entry_safe(ei, n, list->next, link)
72                 oz_elt_info_free(buf, ei);
73         spin_unlock_bh(&buf->lock);
74 }
75
76 int oz_elt_stream_create(struct oz_elt_buf *buf, u8 id, int max_buf_count)
77 {
78         struct oz_elt_stream *st;
79
80         oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
81
82         st = kzalloc(sizeof(struct oz_elt_stream), GFP_ATOMIC);
83         if (st == NULL)
84                 return -ENOMEM;
85         atomic_set(&st->ref_count, 1);
86         st->id = id;
87         st->max_buf_count = max_buf_count;
88         INIT_LIST_HEAD(&st->elt_list);
89         spin_lock_bh(&buf->lock);
90         list_add_tail(&st->link, &buf->stream_list);
91         spin_unlock_bh(&buf->lock);
92         return 0;
93 }
94
95 int oz_elt_stream_delete(struct oz_elt_buf *buf, u8 id)
96 {
97         struct list_head *e, *n;
98         struct oz_elt_stream *st = NULL;
99
100         oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
101         spin_lock_bh(&buf->lock);
102         list_for_each(e, &buf->stream_list) {
103                 st = list_entry(e, struct oz_elt_stream, link);
104                 if (st->id == id) {
105                         list_del(e);
106                         break;
107                 }
108                 st = NULL;
109         }
110         if (!st) {
111                 spin_unlock_bh(&buf->lock);
112                 return -1;
113         }
114         list_for_each_safe(e, n, &st->elt_list) {
115                 struct oz_elt_info *ei =
116                         list_entry(e, struct oz_elt_info, link);
117                 list_del_init(&ei->link);
118                 list_del_init(&ei->link_order);
119                 st->buf_count -= ei->length;
120                 oz_dbg(STREAM, "Stream down: %d %d %d\n",
121                        st->buf_count, ei->length, atomic_read(&st->ref_count));
122                 oz_elt_stream_put(st);
123                 oz_elt_info_free(buf, ei);
124         }
125         spin_unlock_bh(&buf->lock);
126         oz_elt_stream_put(st);
127         return 0;
128 }
129
130 void oz_elt_stream_get(struct oz_elt_stream *st)
131 {
132         atomic_inc(&st->ref_count);
133 }
134
135 void oz_elt_stream_put(struct oz_elt_stream *st)
136 {
137         if (atomic_dec_and_test(&st->ref_count)) {
138                 oz_dbg(ON, "Stream destroyed\n");
139                 kfree(st);
140         }
141 }
142
143 /*
144  * Precondition: Element buffer lock must be held.
145  * If this function fails the caller is responsible for deallocating the elt
146  * info structure.
147  */
148 int oz_queue_elt_info(struct oz_elt_buf *buf, u8 isoc, u8 id,
149         struct oz_elt_info *ei)
150 {
151         struct oz_elt_stream *st = NULL;
152         struct list_head *e;
153
154         if (id) {
155                 list_for_each(e, &buf->stream_list) {
156                         st = list_entry(e, struct oz_elt_stream, link);
157                         if (st->id == id)
158                                 break;
159                 }
160                 if (e == &buf->stream_list) {
161                         /* Stream specified but stream not known so fail.
162                          * Caller deallocates element info. */
163                         return -1;
164                 }
165         }
166         if (st) {
167                 /* If this is an ISOC fixed element that needs a frame number
168                  * then insert that now. Earlier we stored the unit count in
169                  * this field.
170                  */
171                 struct oz_isoc_fixed *body = (struct oz_isoc_fixed *)
172                         &ei->data[sizeof(struct oz_elt)];
173                 if ((body->app_id == OZ_APPID_USB) && (body->type
174                         == OZ_USB_ENDPOINT_DATA) &&
175                         (body->format == OZ_DATA_F_ISOC_FIXED)) {
176                         u8 unit_count = body->frame_number;
177
178                         body->frame_number = st->frame_number;
179                         st->frame_number += unit_count;
180                 }
181                 /* Claim stream and update accounts */
182                 oz_elt_stream_get(st);
183                 ei->stream = st;
184                 st->buf_count += ei->length;
185                 /* Add to list in stream. */
186                 list_add_tail(&ei->link, &st->elt_list);
187                 oz_dbg(STREAM, "Stream up: %d %d\n", st->buf_count, ei->length);
188                 /* Check if we have too much buffered for this stream. If so
189                  * start dropping elements until we are back in bounds.
190                  */
191                 while ((st->buf_count > st->max_buf_count) &&
192                         !list_empty(&st->elt_list)) {
193                         struct oz_elt_info *ei2 =
194                                 list_first_entry(&st->elt_list,
195                                         struct oz_elt_info, link);
196                         list_del_init(&ei2->link);
197                         list_del_init(&ei2->link_order);
198                         st->buf_count -= ei2->length;
199                         oz_elt_info_free(buf, ei2);
200                         oz_elt_stream_put(st);
201                 }
202         }
203         list_add_tail(&ei->link_order, isoc ?
204                 &buf->isoc_list : &buf->order_list);
205         return 0;
206 }
207
208 int oz_select_elts_for_tx(struct oz_elt_buf *buf, u8 isoc, unsigned *len,
209                 unsigned max_len, struct list_head *list)
210 {
211         int count = 0;
212         struct list_head *el;
213         struct oz_elt_info *ei, *n;
214
215         spin_lock_bh(&buf->lock);
216         if (isoc)
217                 el = &buf->isoc_list;
218         else
219                 el = &buf->order_list;
220
221         list_for_each_entry_safe(ei, n, el, link_order) {
222                 if ((*len + ei->length) <= max_len) {
223                         struct oz_app_hdr *app_hdr = (struct oz_app_hdr *)
224                                 &ei->data[sizeof(struct oz_elt)];
225                         app_hdr->elt_seq_num = buf->tx_seq_num[ei->app_id]++;
226                         if (buf->tx_seq_num[ei->app_id] == 0)
227                                 buf->tx_seq_num[ei->app_id] = 1;
228                         *len += ei->length;
229                         list_del(&ei->link);
230                         list_del(&ei->link_order);
231                         if (ei->stream) {
232                                 ei->stream->buf_count -= ei->length;
233                                 oz_dbg(STREAM, "Stream down: %d %d\n",
234                                        ei->stream->buf_count, ei->length);
235                                 oz_elt_stream_put(ei->stream);
236                                 ei->stream = NULL;
237                         }
238                         INIT_LIST_HEAD(&ei->link_order);
239                         list_add_tail(&ei->link, list);
240                         count++;
241                 } else {
242                         break;
243                 }
244         }
245         spin_unlock_bh(&buf->lock);
246         return count;
247 }
248
249 int oz_are_elts_available(struct oz_elt_buf *buf)
250 {
251         return !list_empty(&buf->order_list);
252 }