blob: 01b25da442416fa0216ca3d75d0c8981373b095e [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 */
Chris Kellybc3157d2012-02-20 21:11:37 +00006#include <linux/module.h>
7#include <linux/netdevice.h>
Joe Perchesf724b582013-07-23 13:45:00 +01008#include "ozdbg.h"
Chris Kellybc3157d2012-02-20 21:11:37 +00009#include "ozprotocol.h"
10#include "ozeltbuf.h"
11#include "ozpd.h"
Joe Perchesf724b582013-07-23 13:45:00 +010012
Rupesh Gujare4e7fb822013-08-23 16:11:02 +010013/*
Chris Kellybc3157d2012-02-20 21:11:37 +000014 * Context: softirq-serialized
15 */
Christoph Jaegera7ae7252014-08-04 14:54:51 +020016void oz_elt_buf_init(struct oz_elt_buf *buf)
Chris Kellybc3157d2012-02-20 21:11:37 +000017{
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);
Chris Kellybc3157d2012-02-20 21:11:37 +000022 spin_lock_init(&buf->lock);
Chris Kellybc3157d2012-02-20 21:11:37 +000023}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010024
Rupesh Gujare4e7fb822013-08-23 16:11:02 +010025/*
Chris Kellybc3157d2012-02-20 21:11:37 +000026 * Context: softirq or process
27 */
28void oz_elt_buf_term(struct oz_elt_buf *buf)
29{
Christoph Jaegera87c3802014-08-04 14:54:56 +020030 struct oz_elt_info *ei, *n;
Rupesh Gujare18f81912013-08-13 18:24:21 +010031
Christoph Jaegera87c3802014-08-04 14:54:56 +020032 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);
Chris Kellybc3157d2012-02-20 21:11:37 +000036}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010037
Rupesh Gujare4e7fb822013-08-23 16:11:02 +010038/*
Chris Kellybc3157d2012-02-20 21:11:37 +000039 * Context: softirq or process
40 */
41struct oz_elt_info *oz_elt_info_alloc(struct oz_elt_buf *buf)
42{
Rupesh Gujarea15e0422013-08-13 18:29:24 +010043 struct oz_elt_info *ei;
Rupesh Gujare18f81912013-08-13 18:24:21 +010044
Christoph Jaeger2b8b61a2014-08-08 08:00:42 +020045 ei = kmem_cache_zalloc(oz_elt_info_cache, GFP_ATOMIC);
Chris Kellybc3157d2012-02-20 21:11:37 +000046 if (ei) {
Chris Kellybc3157d2012-02-20 21:11:37 +000047 INIT_LIST_HEAD(&ei->link);
48 INIT_LIST_HEAD(&ei->link_order);
49 }
50 return ei;
51}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010052
Rupesh Gujare4e7fb822013-08-23 16:11:02 +010053/*
Chris Kellybc3157d2012-02-20 21:11:37 +000054 * Precondition: oz_elt_buf.lock must be held.
55 * Context: softirq or process
56 */
57void oz_elt_info_free(struct oz_elt_buf *buf, struct oz_elt_info *ei)
58{
Christoph Jaeger2b8b61a2014-08-08 08:00:42 +020059 if (ei)
60 kmem_cache_free(oz_elt_info_cache, ei);
Chris Kellybc3157d2012-02-20 21:11:37 +000061}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010062
Chris Kellybc3157d2012-02-20 21:11:37 +000063/*------------------------------------------------------------------------------
64 * Context: softirq
65 */
66void oz_elt_info_free_chain(struct oz_elt_buf *buf, struct list_head *list)
67{
Christoph Jaegera87c3802014-08-04 14:54:56 +020068 struct oz_elt_info *ei, *n;
Rupesh Gujare18f81912013-08-13 18:24:21 +010069
Chris Kellybc3157d2012-02-20 21:11:37 +000070 spin_lock_bh(&buf->lock);
Christoph Jaegera87c3802014-08-04 14:54:56 +020071 list_for_each_entry_safe(ei, n, list->next, link)
Chris Kellybc3157d2012-02-20 21:11:37 +000072 oz_elt_info_free(buf, ei);
Chris Kellybc3157d2012-02-20 21:11:37 +000073 spin_unlock_bh(&buf->lock);
74}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010075
Chris Kellybc3157d2012-02-20 21:11:37 +000076int oz_elt_stream_create(struct oz_elt_buf *buf, u8 id, int max_buf_count)
77{
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -080078 struct oz_elt_stream *st;
79
Joe Perchesf724b582013-07-23 13:45:00 +010080 oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -080081
Jie Liu9961d592013-11-01 22:22:08 +080082 st = kzalloc(sizeof(struct oz_elt_stream), GFP_ATOMIC);
Peter Huewe953b1902013-02-15 15:22:24 +010083 if (st == NULL)
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -080084 return -ENOMEM;
Chris Kellybc3157d2012-02-20 21:11:37 +000085 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}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010094
Chris Kellybc3157d2012-02-20 21:11:37 +000095int oz_elt_stream_delete(struct oz_elt_buf *buf, u8 id)
96{
Christoph Jaegera87c3802014-08-04 14:54:56 +020097 struct list_head *e, *n;
Peter Huewe953b1902013-02-15 15:22:24 +010098 struct oz_elt_stream *st = NULL;
Rupesh Gujare18f81912013-08-13 18:24:21 +010099
Joe Perchesf724b582013-07-23 13:45:00 +0100100 oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
Chris Kellybc3157d2012-02-20 21:11:37 +0000101 spin_lock_bh(&buf->lock);
Christoph Jaegera87c3802014-08-04 14:54:56 +0200102 list_for_each(e, &buf->stream_list) {
103 st = list_entry(e, struct oz_elt_stream, link);
Chris Kellybc3157d2012-02-20 21:11:37 +0000104 if (st->id == id) {
105 list_del(e);
106 break;
107 }
Peter Huewe953b1902013-02-15 15:22:24 +0100108 st = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +0000109 }
110 if (!st) {
111 spin_unlock_bh(&buf->lock);
112 return -1;
113 }
Christoph Jaegera87c3802014-08-04 14:54:56 +0200114 list_for_each_safe(e, n, &st->elt_list) {
Chris Kellybc3157d2012-02-20 21:11:37 +0000115 struct oz_elt_info *ei =
Christoph Jaegera87c3802014-08-04 14:54:56 +0200116 list_entry(e, struct oz_elt_info, link);
Chris Kellybc3157d2012-02-20 21:11:37 +0000117 list_del_init(&ei->link);
118 list_del_init(&ei->link_order);
119 st->buf_count -= ei->length;
Joe Perchesf724b582013-07-23 13:45:00 +0100120 oz_dbg(STREAM, "Stream down: %d %d %d\n",
121 st->buf_count, ei->length, atomic_read(&st->ref_count));
Chris Kellybc3157d2012-02-20 21:11:37 +0000122 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}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100129
Chris Kellybc3157d2012-02-20 21:11:37 +0000130void oz_elt_stream_get(struct oz_elt_stream *st)
131{
132 atomic_inc(&st->ref_count);
133}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100134
Chris Kellybc3157d2012-02-20 21:11:37 +0000135void oz_elt_stream_put(struct oz_elt_stream *st)
136{
137 if (atomic_dec_and_test(&st->ref_count)) {
Joe Perchesf724b582013-07-23 13:45:00 +0100138 oz_dbg(ON, "Stream destroyed\n");
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800139 kfree(st);
Chris Kellybc3157d2012-02-20 21:11:37 +0000140 }
141}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100142
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100143/*
Chris Kellybc3157d2012-02-20 21:11:37 +0000144 * Precondition: Element buffer lock must be held.
145 * If this function fails the caller is responsible for deallocating the elt
146 * info structure.
147 */
148int oz_queue_elt_info(struct oz_elt_buf *buf, u8 isoc, u8 id,
149 struct oz_elt_info *ei)
150{
Peter Huewe953b1902013-02-15 15:22:24 +0100151 struct oz_elt_stream *st = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +0000152 struct list_head *e;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100153
Chris Kellybc3157d2012-02-20 21:11:37 +0000154 if (id) {
155 list_for_each(e, &buf->stream_list) {
Christoph Jaegera87c3802014-08-04 14:54:56 +0200156 st = list_entry(e, struct oz_elt_stream, link);
Chris Kellybc3157d2012-02-20 21:11:37 +0000157 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;
Adrian Nicoarace6880e2014-09-08 14:39:58 -0400177
Chris Kellybc3157d2012-02-20 21:11:37 +0000178 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);
Joe Perchesf724b582013-07-23 13:45:00 +0100187 oz_dbg(STREAM, "Stream up: %d %d\n", st->buf_count, ei->length);
Chris Kellybc3157d2012-02-20 21:11:37 +0000188 /* 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}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100207
Chris Kellybc3157d2012-02-20 21:11:37 +0000208int 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;
Chris Kellybc3157d2012-02-20 21:11:37 +0000212 struct list_head *el;
Christoph Jaegera87c3802014-08-04 14:54:56 +0200213 struct oz_elt_info *ei, *n;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100214
Chris Kellybc3157d2012-02-20 21:11:37 +0000215 spin_lock_bh(&buf->lock);
216 if (isoc)
217 el = &buf->isoc_list;
218 else
219 el = &buf->order_list;
Christoph Jaegera87c3802014-08-04 14:54:56 +0200220
221 list_for_each_entry_safe(ei, n, el, link_order) {
Chris Kellybc3157d2012-02-20 21:11:37 +0000222 if ((*len + ei->length) <= max_len) {
Christoph Jaegera87c3802014-08-04 14:54:56 +0200223 struct oz_app_hdr *app_hdr = (struct oz_app_hdr *)
Chris Kellybc3157d2012-02-20 21:11:37 +0000224 &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;
Joe Perchesf724b582013-07-23 13:45:00 +0100233 oz_dbg(STREAM, "Stream down: %d %d\n",
234 ei->stream->buf_count, ei->length);
Chris Kellybc3157d2012-02-20 21:11:37 +0000235 oz_elt_stream_put(ei->stream);
Peter Huewe953b1902013-02-15 15:22:24 +0100236 ei->stream = NULL;
Chris Kellybc3157d2012-02-20 21:11:37 +0000237 }
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}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100248
Chris Kellybc3157d2012-02-20 21:11:37 +0000249int oz_are_elts_available(struct oz_elt_buf *buf)
250{
Christoph Jaegera87c3802014-08-04 14:54:56 +0200251 return !list_empty(&buf->order_list);
Chris Kellybc3157d2012-02-20 21:11:37 +0000252}