]>
git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - drivers/staging/ozwpan/ozeltbuf.c
1 /* -----------------------------------------------------------------------------
2 * Copyright (c) 2011 Ozmo Inc
3 * Released under the GNU General Public License Version 2 (GPLv2).
4 * -----------------------------------------------------------------------------
6 #include <linux/module.h>
7 #include <linux/netdevice.h>
9 #include "ozprotocol.h"
14 * Context: softirq-serialized
16 void oz_elt_buf_init(struct oz_elt_buf
*buf
)
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
);
26 * Context: softirq or process
28 void oz_elt_buf_term(struct oz_elt_buf
*buf
)
30 struct oz_elt_info
*ei
, *n
;
32 list_for_each_entry_safe(ei
, n
, &buf
->isoc_list
, link_order
)
34 list_for_each_entry_safe(ei
, n
, &buf
->order_list
, link_order
)
39 * Context: softirq or process
41 struct oz_elt_info
*oz_elt_info_alloc(struct oz_elt_buf
*buf
)
43 struct oz_elt_info
*ei
;
45 ei
= kmem_cache_zalloc(oz_elt_info_cache
, GFP_ATOMIC
);
47 INIT_LIST_HEAD(&ei
->link
);
48 INIT_LIST_HEAD(&ei
->link_order
);
54 * Precondition: oz_elt_buf.lock must be held.
55 * Context: softirq or process
57 void oz_elt_info_free(struct oz_elt_buf
*buf
, struct oz_elt_info
*ei
)
60 kmem_cache_free(oz_elt_info_cache
, ei
);
63 /*------------------------------------------------------------------------------
66 void oz_elt_info_free_chain(struct oz_elt_buf
*buf
, struct list_head
*list
)
68 struct oz_elt_info
*ei
, *n
;
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
);
76 int oz_elt_stream_create(struct oz_elt_buf
*buf
, u8 id
, int max_buf_count
)
78 struct oz_elt_stream
*st
;
80 oz_dbg(ON
, "%s: (0x%x)\n", __func__
, id
);
82 st
= kzalloc(sizeof(struct oz_elt_stream
), GFP_ATOMIC
);
85 atomic_set(&st
->ref_count
, 1);
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
);
95 int oz_elt_stream_delete(struct oz_elt_buf
*buf
, u8 id
)
97 struct list_head
*e
, *n
;
98 struct oz_elt_stream
*st
= NULL
;
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
);
111 spin_unlock_bh(&buf
->lock
);
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
);
125 spin_unlock_bh(&buf
->lock
);
126 oz_elt_stream_put(st
);
130 void oz_elt_stream_get(struct oz_elt_stream
*st
)
132 atomic_inc(&st
->ref_count
);
135 void oz_elt_stream_put(struct oz_elt_stream
*st
)
137 if (atomic_dec_and_test(&st
->ref_count
)) {
138 oz_dbg(ON
, "Stream destroyed\n");
144 * Precondition: Element buffer lock must be held.
145 * If this function fails the caller is responsible for deallocating the elt
148 int oz_queue_elt_info(struct oz_elt_buf
*buf
, u8 isoc
, u8 id
,
149 struct oz_elt_info
*ei
)
151 struct oz_elt_stream
*st
= NULL
;
155 list_for_each(e
, &buf
->stream_list
) {
156 st
= list_entry(e
, struct oz_elt_stream
, link
);
160 if (e
== &buf
->stream_list
) {
161 /* Stream specified but stream not known so fail.
162 * Caller deallocates element info. */
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
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
;
178 body
->frame_number
= st
->frame_number
;
179 st
->frame_number
+= unit_count
;
181 /* Claim stream and update accounts */
182 oz_elt_stream_get(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.
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
);
203 list_add_tail(&ei
->link_order
, isoc
?
204 &buf
->isoc_list
: &buf
->order_list
);
208 int oz_select_elts_for_tx(struct oz_elt_buf
*buf
, u8 isoc
, unsigned *len
,
209 unsigned max_len
, struct list_head
*list
)
212 struct list_head
*el
;
213 struct oz_elt_info
*ei
, *n
;
215 spin_lock_bh(&buf
->lock
);
217 el
= &buf
->isoc_list
;
219 el
= &buf
->order_list
;
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;
230 list_del(&ei
->link_order
);
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
);
238 INIT_LIST_HEAD(&ei
->link_order
);
239 list_add_tail(&ei
->link
, list
);
245 spin_unlock_bh(&buf
->lock
);
249 int oz_are_elts_available(struct oz_elt_buf
*buf
)
251 return !list_empty(&buf
->order_list
);