Commit | Line | Data |
---|---|---|
bc3157dd CK |
1 | /* ----------------------------------------------------------------------------- |
2 | * Copyright (c) 2011 Ozmo Inc | |
3 | * Released under the GNU General Public License Version 2 (GPLv2). | |
4 | * ----------------------------------------------------------------------------- | |
5 | */ | |
05f608f2 | 6 | |
bc3157dd CK |
7 | #include <linux/module.h> |
8 | #include <linux/timer.h> | |
9 | #include <linux/sched.h> | |
10 | #include <linux/netdevice.h> | |
11 | #include <linux/errno.h> | |
f724b584 | 12 | #include "ozdbg.h" |
bc3157dd CK |
13 | #include "ozprotocol.h" |
14 | #include "ozeltbuf.h" | |
15 | #include "ozpd.h" | |
16 | #include "ozproto.h" | |
bc3157dd CK |
17 | #include "ozcdev.h" |
18 | #include "ozusbsvc.h" | |
19 | #include <asm/unaligned.h> | |
20 | #include <linux/uaccess.h> | |
21 | #include <net/psnap.h> | |
05f608f2 | 22 | |
bc3157dd | 23 | #define OZ_MAX_TX_POOL_SIZE 6 |
6e244a83 | 24 | |
bc3157dd CK |
25 | static struct oz_tx_frame *oz_tx_frame_alloc(struct oz_pd *pd); |
26 | static void oz_tx_frame_free(struct oz_pd *pd, struct oz_tx_frame *f); | |
33e6ada1 | 27 | static void oz_tx_isoc_free(struct oz_pd *pd, struct oz_tx_frame *f); |
bc3157dd CK |
28 | static struct sk_buff *oz_build_frame(struct oz_pd *pd, struct oz_tx_frame *f); |
29 | static int oz_send_isoc_frame(struct oz_pd *pd); | |
30 | static void oz_retire_frame(struct oz_pd *pd, struct oz_tx_frame *f); | |
31 | static void oz_isoc_stream_free(struct oz_isoc_stream *st); | |
33e6ada1 | 32 | static int oz_send_next_queued_frame(struct oz_pd *pd, int more_data); |
bc3157dd CK |
33 | static void oz_isoc_destructor(struct sk_buff *skb); |
34 | static int oz_def_app_init(void); | |
35 | static void oz_def_app_term(void); | |
36 | static int oz_def_app_start(struct oz_pd *pd, int resume); | |
37 | static void oz_def_app_stop(struct oz_pd *pd, int pause); | |
38 | static void oz_def_app_rx(struct oz_pd *pd, struct oz_elt *elt); | |
6e244a83 | 39 | |
4e7fb829 | 40 | /* |
bc3157dd CK |
41 | * Counts the uncompleted isoc frames submitted to netcard. |
42 | */ | |
43 | static atomic_t g_submitted_isoc = ATOMIC_INIT(0); | |
6e244a83 | 44 | |
bc3157dd CK |
45 | /* Application handler functions. |
46 | */ | |
dc7f5b35 | 47 | static const struct oz_app_if g_app_if[OZ_APPID_MAX] = { |
bc3157dd CK |
48 | {oz_usb_init, |
49 | oz_usb_term, | |
50 | oz_usb_start, | |
51 | oz_usb_stop, | |
52 | oz_usb_rx, | |
53 | oz_usb_heartbeat, | |
54 | oz_usb_farewell, | |
55 | OZ_APPID_USB}, | |
56 | ||
57 | {oz_def_app_init, | |
58 | oz_def_app_term, | |
59 | oz_def_app_start, | |
60 | oz_def_app_stop, | |
61 | oz_def_app_rx, | |
86b02be0 PH |
62 | NULL, |
63 | NULL, | |
bc3157dd CK |
64 | OZ_APPID_UNUSED1}, |
65 | ||
66 | {oz_def_app_init, | |
67 | oz_def_app_term, | |
68 | oz_def_app_start, | |
69 | oz_def_app_stop, | |
70 | oz_def_app_rx, | |
86b02be0 PH |
71 | NULL, |
72 | NULL, | |
bc3157dd CK |
73 | OZ_APPID_UNUSED2}, |
74 | ||
75 | {oz_cdev_init, | |
76 | oz_cdev_term, | |
77 | oz_cdev_start, | |
78 | oz_cdev_stop, | |
79 | oz_cdev_rx, | |
86b02be0 PH |
80 | NULL, |
81 | NULL, | |
bc3157dd CK |
82 | OZ_APPID_SERIAL}, |
83 | }; | |
6e244a83 | 84 | |
4e7fb829 | 85 | /* |
bc3157dd CK |
86 | * Context: process |
87 | */ | |
88 | static int oz_def_app_init(void) | |
89 | { | |
90 | return 0; | |
91 | } | |
6e244a83 | 92 | |
4e7fb829 | 93 | /* |
bc3157dd CK |
94 | * Context: process |
95 | */ | |
96 | static void oz_def_app_term(void) | |
97 | { | |
98 | } | |
6e244a83 | 99 | |
4e7fb829 | 100 | /* |
bc3157dd CK |
101 | * Context: softirq |
102 | */ | |
103 | static int oz_def_app_start(struct oz_pd *pd, int resume) | |
104 | { | |
105 | return 0; | |
106 | } | |
6e244a83 | 107 | |
4e7fb829 | 108 | /* |
bc3157dd CK |
109 | * Context: softirq |
110 | */ | |
111 | static void oz_def_app_stop(struct oz_pd *pd, int pause) | |
112 | { | |
113 | } | |
6e244a83 | 114 | |
4e7fb829 | 115 | /* |
bc3157dd CK |
116 | * Context: softirq |
117 | */ | |
118 | static void oz_def_app_rx(struct oz_pd *pd, struct oz_elt *elt) | |
119 | { | |
120 | } | |
6e244a83 | 121 | |
4e7fb829 | 122 | /* |
bc3157dd CK |
123 | * Context: softirq or process |
124 | */ | |
125 | void oz_pd_set_state(struct oz_pd *pd, unsigned state) | |
126 | { | |
127 | pd->state = state; | |
bc3157dd CK |
128 | switch (state) { |
129 | case OZ_PD_S_IDLE: | |
f724b584 | 130 | oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_IDLE\n"); |
bc3157dd CK |
131 | break; |
132 | case OZ_PD_S_CONNECTED: | |
f724b584 | 133 | oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_CONNECTED\n"); |
bc3157dd CK |
134 | break; |
135 | case OZ_PD_S_STOPPED: | |
f724b584 | 136 | oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_STOPPED\n"); |
bc3157dd CK |
137 | break; |
138 | case OZ_PD_S_SLEEP: | |
f724b584 | 139 | oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_SLEEP\n"); |
bc3157dd CK |
140 | break; |
141 | } | |
bc3157dd | 142 | } |
6e244a83 | 143 | |
4e7fb829 | 144 | /* |
bc3157dd CK |
145 | * Context: softirq or process |
146 | */ | |
147 | void oz_pd_get(struct oz_pd *pd) | |
148 | { | |
149 | atomic_inc(&pd->ref_count); | |
150 | } | |
6e244a83 | 151 | |
4e7fb829 | 152 | /* |
bc3157dd CK |
153 | * Context: softirq or process |
154 | */ | |
155 | void oz_pd_put(struct oz_pd *pd) | |
156 | { | |
157 | if (atomic_dec_and_test(&pd->ref_count)) | |
158 | oz_pd_destroy(pd); | |
159 | } | |
6e244a83 | 160 | |
4e7fb829 | 161 | /* |
bc3157dd CK |
162 | * Context: softirq-serialized |
163 | */ | |
dc7f5b35 | 164 | struct oz_pd *oz_pd_alloc(const u8 *mac_addr) |
bc3157dd | 165 | { |
1ec41a31 | 166 | struct oz_pd *pd = kzalloc(sizeof(struct oz_pd), GFP_ATOMIC); |
18f8191e | 167 | |
bc3157dd CK |
168 | if (pd) { |
169 | int i; | |
bc3157dd CK |
170 | atomic_set(&pd->ref_count, 2); |
171 | for (i = 0; i < OZ_APPID_MAX; i++) | |
172 | spin_lock_init(&pd->app_lock[i]); | |
173 | pd->last_rx_pkt_num = 0xffffffff; | |
174 | oz_pd_set_state(pd, OZ_PD_S_IDLE); | |
175 | pd->max_tx_size = OZ_MAX_TX_SIZE; | |
176 | memcpy(pd->mac_addr, mac_addr, ETH_ALEN); | |
177 | if (0 != oz_elt_buf_init(&pd->elt_buff)) { | |
1ec41a31 | 178 | kfree(pd); |
86b02be0 | 179 | pd = NULL; |
bc3157dd CK |
180 | } |
181 | spin_lock_init(&pd->tx_frame_lock); | |
182 | INIT_LIST_HEAD(&pd->tx_queue); | |
183 | INIT_LIST_HEAD(&pd->farewell_list); | |
184 | pd->last_sent_frame = &pd->tx_queue; | |
185 | spin_lock_init(&pd->stream_lock); | |
186 | INIT_LIST_HEAD(&pd->stream_list); | |
8fd07007 RG |
187 | tasklet_init(&pd->heartbeat_tasklet, oz_pd_heartbeat_handler, |
188 | (unsigned long)pd); | |
189 | tasklet_init(&pd->timeout_tasklet, oz_pd_timeout_handler, | |
190 | (unsigned long)pd); | |
191 | hrtimer_init(&pd->heartbeat, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | |
192 | hrtimer_init(&pd->timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | |
193 | pd->heartbeat.function = oz_pd_heartbeat_event; | |
194 | pd->timeout.function = oz_pd_timeout_event; | |
bc3157dd CK |
195 | } |
196 | return pd; | |
197 | } | |
6e244a83 | 198 | |
4e7fb829 | 199 | /* |
bc3157dd CK |
200 | * Context: softirq or process |
201 | */ | |
421acbc2 | 202 | static void oz_pd_free(struct work_struct *work) |
bc3157dd CK |
203 | { |
204 | struct list_head *e; | |
205 | struct oz_tx_frame *f; | |
206 | struct oz_isoc_stream *st; | |
207 | struct oz_farewell *fwell; | |
6af47622 | 208 | struct oz_pd *pd; |
18f8191e | 209 | |
f724b584 | 210 | oz_pd_dbg(pd, ON, "Destroying PD\n"); |
6af47622 | 211 | pd = container_of(work, struct oz_pd, workitem); |
8fd07007 RG |
212 | /*Disable timer tasklets*/ |
213 | tasklet_kill(&pd->heartbeat_tasklet); | |
214 | tasklet_kill(&pd->timeout_tasklet); | |
bc3157dd CK |
215 | /* Delete any streams. |
216 | */ | |
217 | e = pd->stream_list.next; | |
218 | while (e != &pd->stream_list) { | |
219 | st = container_of(e, struct oz_isoc_stream, link); | |
220 | e = e->next; | |
221 | oz_isoc_stream_free(st); | |
222 | } | |
223 | /* Free any queued tx frames. | |
224 | */ | |
225 | e = pd->tx_queue.next; | |
226 | while (e != &pd->tx_queue) { | |
227 | f = container_of(e, struct oz_tx_frame, link); | |
228 | e = e->next; | |
33e6ada1 | 229 | if (f->skb != NULL) |
dd3cef0f | 230 | kfree_skb(f->skb); |
bc3157dd CK |
231 | oz_retire_frame(pd, f); |
232 | } | |
233 | oz_elt_buf_term(&pd->elt_buff); | |
234 | /* Free any farewells. | |
235 | */ | |
236 | e = pd->farewell_list.next; | |
237 | while (e != &pd->farewell_list) { | |
238 | fwell = container_of(e, struct oz_farewell, link); | |
239 | e = e->next; | |
1ec41a31 | 240 | kfree(fwell); |
bc3157dd CK |
241 | } |
242 | /* Deallocate all frames in tx pool. | |
243 | */ | |
244 | while (pd->tx_pool) { | |
245 | e = pd->tx_pool; | |
246 | pd->tx_pool = e->next; | |
1ec41a31 | 247 | kfree(container_of(e, struct oz_tx_frame, link)); |
bc3157dd CK |
248 | } |
249 | if (pd->net_dev) | |
250 | dev_put(pd->net_dev); | |
1ec41a31 | 251 | kfree(pd); |
bc3157dd | 252 | } |
6e244a83 | 253 | |
4e7fb829 | 254 | /* |
6af47622 RG |
255 | * Context: softirq or Process |
256 | */ | |
257 | void oz_pd_destroy(struct oz_pd *pd) | |
258 | { | |
6af47622 RG |
259 | if (hrtimer_active(&pd->timeout)) |
260 | hrtimer_cancel(&pd->timeout); | |
261 | if (hrtimer_active(&pd->heartbeat)) | |
262 | hrtimer_cancel(&pd->heartbeat); | |
263 | ||
6af47622 | 264 | INIT_WORK(&pd->workitem, oz_pd_free); |
dfc065f1 | 265 | if (!schedule_work(&pd->workitem)) |
6af47622 RG |
266 | oz_pd_dbg(pd, ON, "failed to schedule workitem\n"); |
267 | } | |
268 | ||
4e7fb829 | 269 | /* |
bc3157dd CK |
270 | * Context: softirq-serialized |
271 | */ | |
272 | int oz_services_start(struct oz_pd *pd, u16 apps, int resume) | |
273 | { | |
dc7f5b35 | 274 | const struct oz_app_if *ai; |
bc3157dd | 275 | int rc = 0; |
18f8191e | 276 | |
f724b584 | 277 | oz_pd_dbg(pd, ON, "%s: (0x%x) resume(%d)\n", __func__, apps, resume); |
bc3157dd CK |
278 | for (ai = g_app_if; ai < &g_app_if[OZ_APPID_MAX]; ai++) { |
279 | if (apps & (1<<ai->app_id)) { | |
280 | if (ai->start(pd, resume)) { | |
281 | rc = -1; | |
f724b584 JP |
282 | oz_pd_dbg(pd, ON, |
283 | "Unable to start service %d\n", | |
284 | ai->app_id); | |
bc3157dd CK |
285 | break; |
286 | } | |
287 | oz_polling_lock_bh(); | |
288 | pd->total_apps |= (1<<ai->app_id); | |
289 | if (resume) | |
290 | pd->paused_apps &= ~(1<<ai->app_id); | |
291 | oz_polling_unlock_bh(); | |
292 | } | |
293 | } | |
294 | return rc; | |
295 | } | |
6e244a83 | 296 | |
4e7fb829 | 297 | /* |
bc3157dd CK |
298 | * Context: softirq or process |
299 | */ | |
300 | void oz_services_stop(struct oz_pd *pd, u16 apps, int pause) | |
301 | { | |
dc7f5b35 | 302 | const struct oz_app_if *ai; |
18f8191e | 303 | |
f724b584 | 304 | oz_pd_dbg(pd, ON, "%s: (0x%x) pause(%d)\n", __func__, apps, pause); |
bc3157dd CK |
305 | for (ai = g_app_if; ai < &g_app_if[OZ_APPID_MAX]; ai++) { |
306 | if (apps & (1<<ai->app_id)) { | |
307 | oz_polling_lock_bh(); | |
308 | if (pause) { | |
309 | pd->paused_apps |= (1<<ai->app_id); | |
310 | } else { | |
311 | pd->total_apps &= ~(1<<ai->app_id); | |
312 | pd->paused_apps &= ~(1<<ai->app_id); | |
313 | } | |
314 | oz_polling_unlock_bh(); | |
315 | ai->stop(pd, pause); | |
316 | } | |
317 | } | |
318 | } | |
6e244a83 | 319 | |
4e7fb829 | 320 | /* |
bc3157dd CK |
321 | * Context: softirq |
322 | */ | |
323 | void oz_pd_heartbeat(struct oz_pd *pd, u16 apps) | |
324 | { | |
dc7f5b35 | 325 | const struct oz_app_if *ai; |
bc3157dd | 326 | int more = 0; |
18f8191e | 327 | |
bc3157dd CK |
328 | for (ai = g_app_if; ai < &g_app_if[OZ_APPID_MAX]; ai++) { |
329 | if (ai->heartbeat && (apps & (1<<ai->app_id))) { | |
330 | if (ai->heartbeat(pd)) | |
331 | more = 1; | |
332 | } | |
333 | } | |
8fd07007 RG |
334 | if ((!more) && (hrtimer_active(&pd->heartbeat))) |
335 | hrtimer_cancel(&pd->heartbeat); | |
bc3157dd CK |
336 | if (pd->mode & OZ_F_ISOC_ANYTIME) { |
337 | int count = 8; | |
338 | while (count-- && (oz_send_isoc_frame(pd) >= 0)) | |
339 | ; | |
340 | } | |
341 | } | |
6e244a83 | 342 | |
4e7fb829 | 343 | /* |
bc3157dd CK |
344 | * Context: softirq or process |
345 | */ | |
346 | void oz_pd_stop(struct oz_pd *pd) | |
347 | { | |
a15e042e | 348 | u16 stop_apps; |
18f8191e | 349 | |
f724b584 | 350 | oz_dbg(ON, "oz_pd_stop() State = 0x%x\n", pd->state); |
bc3157dd CK |
351 | oz_pd_indicate_farewells(pd); |
352 | oz_polling_lock_bh(); | |
353 | stop_apps = pd->total_apps; | |
354 | pd->total_apps = 0; | |
355 | pd->paused_apps = 0; | |
356 | oz_polling_unlock_bh(); | |
357 | oz_services_stop(pd, stop_apps, 0); | |
358 | oz_polling_lock_bh(); | |
359 | oz_pd_set_state(pd, OZ_PD_S_STOPPED); | |
360 | /* Remove from PD list.*/ | |
361 | list_del(&pd->link); | |
362 | oz_polling_unlock_bh(); | |
f724b584 | 363 | oz_dbg(ON, "pd ref count = %d\n", atomic_read(&pd->ref_count)); |
bc3157dd CK |
364 | oz_pd_put(pd); |
365 | } | |
6e244a83 | 366 | |
4e7fb829 | 367 | /* |
bc3157dd CK |
368 | * Context: softirq |
369 | */ | |
370 | int oz_pd_sleep(struct oz_pd *pd) | |
371 | { | |
372 | int do_stop = 0; | |
a15e042e | 373 | u16 stop_apps; |
18f8191e | 374 | |
bc3157dd CK |
375 | oz_polling_lock_bh(); |
376 | if (pd->state & (OZ_PD_S_SLEEP | OZ_PD_S_STOPPED)) { | |
377 | oz_polling_unlock_bh(); | |
378 | return 0; | |
379 | } | |
8fd07007 | 380 | if (pd->keep_alive && pd->session_id) |
bc3157dd | 381 | oz_pd_set_state(pd, OZ_PD_S_SLEEP); |
8fd07007 | 382 | else |
bc3157dd | 383 | do_stop = 1; |
8fd07007 | 384 | |
bc3157dd CK |
385 | stop_apps = pd->total_apps; |
386 | oz_polling_unlock_bh(); | |
387 | if (do_stop) { | |
388 | oz_pd_stop(pd); | |
389 | } else { | |
390 | oz_services_stop(pd, stop_apps, 1); | |
8fd07007 | 391 | oz_timer_add(pd, OZ_TIMER_STOP, pd->keep_alive); |
bc3157dd CK |
392 | } |
393 | return do_stop; | |
394 | } | |
6e244a83 | 395 | |
4e7fb829 | 396 | /* |
bc3157dd CK |
397 | * Context: softirq |
398 | */ | |
399 | static struct oz_tx_frame *oz_tx_frame_alloc(struct oz_pd *pd) | |
400 | { | |
86b02be0 | 401 | struct oz_tx_frame *f = NULL; |
18f8191e | 402 | |
bc3157dd CK |
403 | spin_lock_bh(&pd->tx_frame_lock); |
404 | if (pd->tx_pool) { | |
405 | f = container_of(pd->tx_pool, struct oz_tx_frame, link); | |
406 | pd->tx_pool = pd->tx_pool->next; | |
407 | pd->tx_pool_count--; | |
408 | } | |
409 | spin_unlock_bh(&pd->tx_frame_lock); | |
86b02be0 | 410 | if (f == NULL) |
1ec41a31 | 411 | f = kmalloc(sizeof(struct oz_tx_frame), GFP_ATOMIC); |
bc3157dd CK |
412 | if (f) { |
413 | f->total_size = sizeof(struct oz_hdr); | |
414 | INIT_LIST_HEAD(&f->link); | |
415 | INIT_LIST_HEAD(&f->elt_list); | |
416 | } | |
417 | return f; | |
418 | } | |
6e244a83 | 419 | |
4e7fb829 | 420 | /* |
33e6ada1 RG |
421 | * Context: softirq or process |
422 | */ | |
423 | static void oz_tx_isoc_free(struct oz_pd *pd, struct oz_tx_frame *f) | |
424 | { | |
425 | pd->nb_queued_isoc_frames--; | |
426 | list_del_init(&f->link); | |
427 | if (pd->tx_pool_count < OZ_MAX_TX_POOL_SIZE) { | |
428 | f->link.next = pd->tx_pool; | |
429 | pd->tx_pool = &f->link; | |
430 | pd->tx_pool_count++; | |
431 | } else { | |
432 | kfree(f); | |
433 | } | |
f724b584 JP |
434 | oz_dbg(TX_FRAMES, "Releasing ISOC Frame isoc_nb= %d\n", |
435 | pd->nb_queued_isoc_frames); | |
33e6ada1 | 436 | } |
6e244a83 | 437 | |
4e7fb829 | 438 | /* |
bc3157dd CK |
439 | * Context: softirq or process |
440 | */ | |
441 | static void oz_tx_frame_free(struct oz_pd *pd, struct oz_tx_frame *f) | |
442 | { | |
443 | spin_lock_bh(&pd->tx_frame_lock); | |
444 | if (pd->tx_pool_count < OZ_MAX_TX_POOL_SIZE) { | |
445 | f->link.next = pd->tx_pool; | |
446 | pd->tx_pool = &f->link; | |
447 | pd->tx_pool_count++; | |
86b02be0 | 448 | f = NULL; |
bc3157dd CK |
449 | } |
450 | spin_unlock_bh(&pd->tx_frame_lock); | |
b150718e | 451 | kfree(f); |
bc3157dd | 452 | } |
6e244a83 | 453 | |
4e7fb829 | 454 | /* |
33e6ada1 RG |
455 | * Context: softirq-serialized |
456 | */ | |
a7f74c30 | 457 | static void oz_set_more_bit(struct sk_buff *skb) |
33e6ada1 RG |
458 | { |
459 | struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb); | |
18f8191e | 460 | |
33e6ada1 RG |
461 | oz_hdr->control |= OZ_F_MORE_DATA; |
462 | } | |
6e244a83 | 463 | |
4e7fb829 | 464 | /* |
00ec12b8 RG |
465 | * Context: softirq-serialized |
466 | */ | |
a7f74c30 | 467 | static void oz_set_last_pkt_nb(struct oz_pd *pd, struct sk_buff *skb) |
00ec12b8 RG |
468 | { |
469 | struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb); | |
18f8191e | 470 | |
00ec12b8 RG |
471 | oz_hdr->last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK; |
472 | } | |
6e244a83 | 473 | |
4e7fb829 | 474 | /* |
bc3157dd CK |
475 | * Context: softirq |
476 | */ | |
477 | int oz_prepare_frame(struct oz_pd *pd, int empty) | |
478 | { | |
479 | struct oz_tx_frame *f; | |
18f8191e | 480 | |
bc3157dd CK |
481 | if ((pd->mode & OZ_MODE_MASK) != OZ_MODE_TRIGGERED) |
482 | return -1; | |
483 | if (pd->nb_queued_frames >= OZ_MAX_QUEUED_FRAMES) | |
484 | return -1; | |
485 | if (!empty && !oz_are_elts_available(&pd->elt_buff)) | |
486 | return -1; | |
487 | f = oz_tx_frame_alloc(pd); | |
86b02be0 | 488 | if (f == NULL) |
bc3157dd | 489 | return -1; |
33e6ada1 | 490 | f->skb = NULL; |
bc3157dd CK |
491 | f->hdr.control = |
492 | (OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT) | OZ_F_ACK_REQUESTED; | |
493 | ++pd->last_tx_pkt_num; | |
494 | put_unaligned(cpu_to_le32(pd->last_tx_pkt_num), &f->hdr.pkt_num); | |
495 | if (empty == 0) { | |
496 | oz_select_elts_for_tx(&pd->elt_buff, 0, &f->total_size, | |
497 | pd->max_tx_size, &f->elt_list); | |
498 | } | |
499 | spin_lock(&pd->tx_frame_lock); | |
500 | list_add_tail(&f->link, &pd->tx_queue); | |
501 | pd->nb_queued_frames++; | |
502 | spin_unlock(&pd->tx_frame_lock); | |
503 | return 0; | |
504 | } | |
6e244a83 | 505 | |
4e7fb829 | 506 | /* |
bc3157dd CK |
507 | * Context: softirq-serialized |
508 | */ | |
509 | static struct sk_buff *oz_build_frame(struct oz_pd *pd, struct oz_tx_frame *f) | |
510 | { | |
86b02be0 | 511 | struct sk_buff *skb; |
bc3157dd CK |
512 | struct net_device *dev = pd->net_dev; |
513 | struct oz_hdr *oz_hdr; | |
514 | struct oz_elt *elt; | |
515 | struct list_head *e; | |
18f8191e | 516 | |
bc3157dd CK |
517 | /* Allocate skb with enough space for the lower layers as well |
518 | * as the space we need. | |
519 | */ | |
ec0ee957 | 520 | skb = alloc_skb(f->total_size + OZ_ALLOCATED_SPACE(dev), GFP_ATOMIC); |
86b02be0 PH |
521 | if (skb == NULL) |
522 | return NULL; | |
bc3157dd CK |
523 | /* Reserve the head room for lower layers. |
524 | */ | |
525 | skb_reserve(skb, LL_RESERVED_SPACE(dev)); | |
526 | skb_reset_network_header(skb); | |
527 | skb->dev = dev; | |
528 | skb->protocol = htons(OZ_ETHERTYPE); | |
529 | if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr, | |
530 | dev->dev_addr, skb->len) < 0) | |
531 | goto fail; | |
532 | /* Push the tail to the end of the area we are going to copy to. | |
533 | */ | |
534 | oz_hdr = (struct oz_hdr *)skb_put(skb, f->total_size); | |
535 | f->hdr.last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK; | |
536 | memcpy(oz_hdr, &f->hdr, sizeof(struct oz_hdr)); | |
537 | /* Copy the elements into the frame body. | |
538 | */ | |
539 | elt = (struct oz_elt *)(oz_hdr+1); | |
540 | for (e = f->elt_list.next; e != &f->elt_list; e = e->next) { | |
541 | struct oz_elt_info *ei; | |
542 | ei = container_of(e, struct oz_elt_info, link); | |
543 | memcpy(elt, ei->data, ei->length); | |
544 | elt = oz_next_elt(elt); | |
545 | } | |
546 | return skb; | |
547 | fail: | |
dd3cef0f | 548 | kfree_skb(skb); |
86b02be0 | 549 | return NULL; |
bc3157dd | 550 | } |
6e244a83 | 551 | |
4e7fb829 | 552 | /* |
bc3157dd CK |
553 | * Context: softirq or process |
554 | */ | |
555 | static void oz_retire_frame(struct oz_pd *pd, struct oz_tx_frame *f) | |
556 | { | |
557 | struct list_head *e; | |
558 | struct oz_elt_info *ei; | |
18f8191e | 559 | |
bc3157dd CK |
560 | e = f->elt_list.next; |
561 | while (e != &f->elt_list) { | |
562 | ei = container_of(e, struct oz_elt_info, link); | |
563 | e = e->next; | |
564 | list_del_init(&ei->link); | |
565 | if (ei->callback) | |
566 | ei->callback(pd, ei->context); | |
567 | spin_lock_bh(&pd->elt_buff.lock); | |
568 | oz_elt_info_free(&pd->elt_buff, ei); | |
569 | spin_unlock_bh(&pd->elt_buff.lock); | |
570 | } | |
571 | oz_tx_frame_free(pd, f); | |
572 | if (pd->elt_buff.free_elts > pd->elt_buff.max_free_elts) | |
573 | oz_trim_elt_pool(&pd->elt_buff); | |
574 | } | |
6e244a83 | 575 | |
4e7fb829 | 576 | /* |
bc3157dd CK |
577 | * Context: softirq-serialized |
578 | */ | |
33e6ada1 | 579 | static int oz_send_next_queued_frame(struct oz_pd *pd, int more_data) |
bc3157dd CK |
580 | { |
581 | struct sk_buff *skb; | |
582 | struct oz_tx_frame *f; | |
583 | struct list_head *e; | |
18f8191e | 584 | |
bc3157dd CK |
585 | spin_lock(&pd->tx_frame_lock); |
586 | e = pd->last_sent_frame->next; | |
587 | if (e == &pd->tx_queue) { | |
588 | spin_unlock(&pd->tx_frame_lock); | |
589 | return -1; | |
590 | } | |
bc3157dd | 591 | f = container_of(e, struct oz_tx_frame, link); |
33e6ada1 RG |
592 | |
593 | if (f->skb != NULL) { | |
594 | skb = f->skb; | |
595 | oz_tx_isoc_free(pd, f); | |
596 | spin_unlock(&pd->tx_frame_lock); | |
597 | if (more_data) | |
598 | oz_set_more_bit(skb); | |
00ec12b8 | 599 | oz_set_last_pkt_nb(pd, skb); |
33e6ada1 RG |
600 | if ((int)atomic_read(&g_submitted_isoc) < |
601 | OZ_MAX_SUBMITTED_ISOC) { | |
602 | if (dev_queue_xmit(skb) < 0) { | |
f724b584 | 603 | oz_dbg(TX_FRAMES, "Dropping ISOC Frame\n"); |
33e6ada1 RG |
604 | return -1; |
605 | } | |
606 | atomic_inc(&g_submitted_isoc); | |
f724b584 JP |
607 | oz_dbg(TX_FRAMES, "Sending ISOC Frame, nb_isoc= %d\n", |
608 | pd->nb_queued_isoc_frames); | |
33e6ada1 RG |
609 | return 0; |
610 | } else { | |
dd3cef0f | 611 | kfree_skb(skb); |
f724b584 | 612 | oz_dbg(TX_FRAMES, "Dropping ISOC Frame>\n"); |
33e6ada1 RG |
613 | return -1; |
614 | } | |
615 | } | |
616 | ||
617 | pd->last_sent_frame = e; | |
bc3157dd CK |
618 | skb = oz_build_frame(pd, f); |
619 | spin_unlock(&pd->tx_frame_lock); | |
37bc8f78 RG |
620 | if (!skb) |
621 | return -1; | |
33e6ada1 RG |
622 | if (more_data) |
623 | oz_set_more_bit(skb); | |
f724b584 | 624 | oz_dbg(TX_FRAMES, "TX frame PN=0x%x\n", f->hdr.pkt_num); |
37bc8f78 RG |
625 | if (dev_queue_xmit(skb) < 0) |
626 | return -1; | |
33e6ada1 | 627 | |
bc3157dd CK |
628 | return 0; |
629 | } | |
6e244a83 | 630 | |
4e7fb829 | 631 | /* |
bc3157dd CK |
632 | * Context: softirq-serialized |
633 | */ | |
634 | void oz_send_queued_frames(struct oz_pd *pd, int backlog) | |
635 | { | |
33e6ada1 RG |
636 | while (oz_prepare_frame(pd, 0) >= 0) |
637 | backlog++; | |
638 | ||
639 | switch (pd->mode & (OZ_F_ISOC_NO_ELTS | OZ_F_ISOC_ANYTIME)) { | |
640 | ||
641 | case OZ_F_ISOC_NO_ELTS: { | |
642 | backlog += pd->nb_queued_isoc_frames; | |
643 | if (backlog <= 0) | |
644 | goto out; | |
645 | if (backlog > OZ_MAX_SUBMITTED_ISOC) | |
646 | backlog = OZ_MAX_SUBMITTED_ISOC; | |
647 | break; | |
bc3157dd | 648 | } |
33e6ada1 RG |
649 | case OZ_NO_ELTS_ANYTIME: { |
650 | if ((backlog <= 0) && (pd->isoc_sent == 0)) | |
651 | goto out; | |
652 | break; | |
653 | } | |
654 | default: { | |
655 | if (backlog <= 0) | |
656 | goto out; | |
657 | break; | |
658 | } | |
659 | } | |
660 | while (backlog--) { | |
661 | if (oz_send_next_queued_frame(pd, backlog) < 0) | |
662 | break; | |
bc3157dd | 663 | } |
33e6ada1 RG |
664 | return; |
665 | ||
666 | out: oz_prepare_frame(pd, 1); | |
667 | oz_send_next_queued_frame(pd, 0); | |
bc3157dd | 668 | } |
6e244a83 | 669 | |
4e7fb829 | 670 | /* |
bc3157dd CK |
671 | * Context: softirq |
672 | */ | |
673 | static int oz_send_isoc_frame(struct oz_pd *pd) | |
674 | { | |
86b02be0 | 675 | struct sk_buff *skb; |
bc3157dd CK |
676 | struct net_device *dev = pd->net_dev; |
677 | struct oz_hdr *oz_hdr; | |
678 | struct oz_elt *elt; | |
679 | struct list_head *e; | |
680 | struct list_head list; | |
681 | int total_size = sizeof(struct oz_hdr); | |
18f8191e | 682 | |
bc3157dd CK |
683 | INIT_LIST_HEAD(&list); |
684 | ||
685 | oz_select_elts_for_tx(&pd->elt_buff, 1, &total_size, | |
686 | pd->max_tx_size, &list); | |
687 | if (list.next == &list) | |
688 | return 0; | |
ec0ee957 | 689 | skb = alloc_skb(total_size + OZ_ALLOCATED_SPACE(dev), GFP_ATOMIC); |
86b02be0 | 690 | if (skb == NULL) { |
f724b584 | 691 | oz_dbg(ON, "Cannot alloc skb\n"); |
bc3157dd CK |
692 | oz_elt_info_free_chain(&pd->elt_buff, &list); |
693 | return -1; | |
694 | } | |
695 | skb_reserve(skb, LL_RESERVED_SPACE(dev)); | |
696 | skb_reset_network_header(skb); | |
697 | skb->dev = dev; | |
698 | skb->protocol = htons(OZ_ETHERTYPE); | |
699 | if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr, | |
700 | dev->dev_addr, skb->len) < 0) { | |
dd3cef0f | 701 | kfree_skb(skb); |
bc3157dd CK |
702 | return -1; |
703 | } | |
704 | oz_hdr = (struct oz_hdr *)skb_put(skb, total_size); | |
705 | oz_hdr->control = (OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT) | OZ_F_ISOC; | |
706 | oz_hdr->last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK; | |
707 | elt = (struct oz_elt *)(oz_hdr+1); | |
708 | ||
709 | for (e = list.next; e != &list; e = e->next) { | |
710 | struct oz_elt_info *ei; | |
711 | ei = container_of(e, struct oz_elt_info, link); | |
712 | memcpy(elt, ei->data, ei->length); | |
713 | elt = oz_next_elt(elt); | |
714 | } | |
bc3157dd CK |
715 | dev_queue_xmit(skb); |
716 | oz_elt_info_free_chain(&pd->elt_buff, &list); | |
717 | return 0; | |
718 | } | |
6e244a83 | 719 | |
4e7fb829 | 720 | /* |
bc3157dd CK |
721 | * Context: softirq-serialized |
722 | */ | |
723 | void oz_retire_tx_frames(struct oz_pd *pd, u8 lpn) | |
724 | { | |
725 | struct list_head *e; | |
726 | struct oz_tx_frame *f; | |
86b02be0 PH |
727 | struct list_head *first = NULL; |
728 | struct list_head *last = NULL; | |
bc3157dd CK |
729 | u8 diff; |
730 | u32 pkt_num; | |
731 | ||
732 | spin_lock(&pd->tx_frame_lock); | |
733 | e = pd->tx_queue.next; | |
734 | while (e != &pd->tx_queue) { | |
735 | f = container_of(e, struct oz_tx_frame, link); | |
736 | pkt_num = le32_to_cpu(get_unaligned(&f->hdr.pkt_num)); | |
737 | diff = (lpn - (pkt_num & OZ_LAST_PN_MASK)) & OZ_LAST_PN_MASK; | |
33e6ada1 | 738 | if ((diff > OZ_LAST_PN_HALF_CYCLE) || (pkt_num == 0)) |
bc3157dd | 739 | break; |
f724b584 JP |
740 | oz_dbg(TX_FRAMES, "Releasing pkt_num= %u, nb= %d\n", |
741 | pkt_num, pd->nb_queued_frames); | |
86b02be0 | 742 | if (first == NULL) |
bc3157dd CK |
743 | first = e; |
744 | last = e; | |
745 | e = e->next; | |
746 | pd->nb_queued_frames--; | |
747 | } | |
748 | if (first) { | |
749 | last->next->prev = &pd->tx_queue; | |
750 | pd->tx_queue.next = last->next; | |
86b02be0 | 751 | last->next = NULL; |
bc3157dd CK |
752 | } |
753 | pd->last_sent_frame = &pd->tx_queue; | |
754 | spin_unlock(&pd->tx_frame_lock); | |
755 | while (first) { | |
756 | f = container_of(first, struct oz_tx_frame, link); | |
757 | first = first->next; | |
758 | oz_retire_frame(pd, f); | |
759 | } | |
760 | } | |
6e244a83 | 761 | |
4e7fb829 | 762 | /* |
bc3157dd CK |
763 | * Precondition: stream_lock must be held. |
764 | * Context: softirq | |
765 | */ | |
766 | static struct oz_isoc_stream *pd_stream_find(struct oz_pd *pd, u8 ep_num) | |
767 | { | |
768 | struct list_head *e; | |
769 | struct oz_isoc_stream *st; | |
18f8191e | 770 | |
bc3157dd CK |
771 | list_for_each(e, &pd->stream_list) { |
772 | st = container_of(e, struct oz_isoc_stream, link); | |
773 | if (st->ep_num == ep_num) | |
774 | return st; | |
775 | } | |
86b02be0 | 776 | return NULL; |
bc3157dd | 777 | } |
6e244a83 | 778 | |
4e7fb829 | 779 | /* |
bc3157dd CK |
780 | * Context: softirq |
781 | */ | |
782 | int oz_isoc_stream_create(struct oz_pd *pd, u8 ep_num) | |
783 | { | |
784 | struct oz_isoc_stream *st = | |
1ec41a31 | 785 | kzalloc(sizeof(struct oz_isoc_stream), GFP_ATOMIC); |
bc3157dd | 786 | if (!st) |
1ec41a31 | 787 | return -ENOMEM; |
bc3157dd CK |
788 | st->ep_num = ep_num; |
789 | spin_lock_bh(&pd->stream_lock); | |
790 | if (!pd_stream_find(pd, ep_num)) { | |
791 | list_add(&st->link, &pd->stream_list); | |
86b02be0 | 792 | st = NULL; |
bc3157dd CK |
793 | } |
794 | spin_unlock_bh(&pd->stream_lock); | |
b150718e | 795 | kfree(st); |
bc3157dd CK |
796 | return 0; |
797 | } | |
6e244a83 | 798 | |
4e7fb829 | 799 | /* |
bc3157dd CK |
800 | * Context: softirq or process |
801 | */ | |
802 | static void oz_isoc_stream_free(struct oz_isoc_stream *st) | |
803 | { | |
dd3cef0f | 804 | kfree_skb(st->skb); |
1ec41a31 | 805 | kfree(st); |
bc3157dd | 806 | } |
6e244a83 | 807 | |
4e7fb829 | 808 | /* |
bc3157dd CK |
809 | * Context: softirq |
810 | */ | |
811 | int oz_isoc_stream_delete(struct oz_pd *pd, u8 ep_num) | |
812 | { | |
813 | struct oz_isoc_stream *st; | |
18f8191e | 814 | |
bc3157dd CK |
815 | spin_lock_bh(&pd->stream_lock); |
816 | st = pd_stream_find(pd, ep_num); | |
817 | if (st) | |
818 | list_del(&st->link); | |
819 | spin_unlock_bh(&pd->stream_lock); | |
820 | if (st) | |
821 | oz_isoc_stream_free(st); | |
822 | return 0; | |
823 | } | |
6e244a83 | 824 | |
4e7fb829 | 825 | /* |
bc3157dd CK |
826 | * Context: any |
827 | */ | |
828 | static void oz_isoc_destructor(struct sk_buff *skb) | |
829 | { | |
830 | atomic_dec(&g_submitted_isoc); | |
bc3157dd | 831 | } |
6e244a83 | 832 | |
4e7fb829 | 833 | /* |
bc3157dd CK |
834 | * Context: softirq |
835 | */ | |
dc7f5b35 | 836 | int oz_send_isoc_unit(struct oz_pd *pd, u8 ep_num, const u8 *data, int len) |
bc3157dd CK |
837 | { |
838 | struct net_device *dev = pd->net_dev; | |
839 | struct oz_isoc_stream *st; | |
840 | u8 nb_units = 0; | |
86b02be0 PH |
841 | struct sk_buff *skb = NULL; |
842 | struct oz_hdr *oz_hdr = NULL; | |
bc3157dd | 843 | int size = 0; |
18f8191e | 844 | |
bc3157dd CK |
845 | spin_lock_bh(&pd->stream_lock); |
846 | st = pd_stream_find(pd, ep_num); | |
847 | if (st) { | |
848 | skb = st->skb; | |
86b02be0 | 849 | st->skb = NULL; |
bc3157dd CK |
850 | nb_units = st->nb_units; |
851 | st->nb_units = 0; | |
852 | oz_hdr = st->oz_hdr; | |
853 | size = st->size; | |
854 | } | |
855 | spin_unlock_bh(&pd->stream_lock); | |
856 | if (!st) | |
857 | return 0; | |
858 | if (!skb) { | |
859 | /* Allocate enough space for max size frame. */ | |
ec0ee957 GKH |
860 | skb = alloc_skb(pd->max_tx_size + OZ_ALLOCATED_SPACE(dev), |
861 | GFP_ATOMIC); | |
86b02be0 | 862 | if (skb == NULL) |
bc3157dd CK |
863 | return 0; |
864 | /* Reserve the head room for lower layers. */ | |
865 | skb_reserve(skb, LL_RESERVED_SPACE(dev)); | |
866 | skb_reset_network_header(skb); | |
867 | skb->dev = dev; | |
868 | skb->protocol = htons(OZ_ETHERTYPE); | |
86c948b4 RG |
869 | /* For audio packet set priority to AC_VO */ |
870 | skb->priority = 0x7; | |
bc3157dd CK |
871 | size = sizeof(struct oz_hdr) + sizeof(struct oz_isoc_large); |
872 | oz_hdr = (struct oz_hdr *)skb_put(skb, size); | |
873 | } | |
874 | memcpy(skb_put(skb, len), data, len); | |
875 | size += len; | |
876 | if (++nb_units < pd->ms_per_isoc) { | |
877 | spin_lock_bh(&pd->stream_lock); | |
878 | st->skb = skb; | |
879 | st->nb_units = nb_units; | |
880 | st->oz_hdr = oz_hdr; | |
881 | st->size = size; | |
882 | spin_unlock_bh(&pd->stream_lock); | |
883 | } else { | |
884 | struct oz_hdr oz; | |
885 | struct oz_isoc_large iso; | |
886 | spin_lock_bh(&pd->stream_lock); | |
887 | iso.frame_number = st->frame_num; | |
888 | st->frame_num += nb_units; | |
889 | spin_unlock_bh(&pd->stream_lock); | |
890 | oz.control = | |
891 | (OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT) | OZ_F_ISOC; | |
892 | oz.last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK; | |
893 | oz.pkt_num = 0; | |
894 | iso.endpoint = ep_num; | |
895 | iso.format = OZ_DATA_F_ISOC_LARGE; | |
896 | iso.ms_data = nb_units; | |
897 | memcpy(oz_hdr, &oz, sizeof(oz)); | |
898 | memcpy(oz_hdr+1, &iso, sizeof(iso)); | |
899 | if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr, | |
33e6ada1 RG |
900 | dev->dev_addr, skb->len) < 0) |
901 | goto out; | |
902 | ||
903 | skb->destructor = oz_isoc_destructor; | |
904 | /*Queue for Xmit if mode is not ANYTIME*/ | |
905 | if (!(pd->mode & OZ_F_ISOC_ANYTIME)) { | |
906 | struct oz_tx_frame *isoc_unit = NULL; | |
907 | int nb = pd->nb_queued_isoc_frames; | |
86d03a0f | 908 | if (nb >= pd->isoc_latency) { |
2dc2ee5c RG |
909 | struct list_head *e; |
910 | struct oz_tx_frame *f; | |
f724b584 JP |
911 | oz_dbg(TX_FRAMES, "Dropping ISOC Unit nb= %d\n", |
912 | nb); | |
2dc2ee5c RG |
913 | spin_lock(&pd->tx_frame_lock); |
914 | list_for_each(e, &pd->tx_queue) { | |
915 | f = container_of(e, struct oz_tx_frame, | |
916 | link); | |
917 | if (f->skb != NULL) { | |
918 | oz_tx_isoc_free(pd, f); | |
919 | break; | |
920 | } | |
921 | } | |
922 | spin_unlock(&pd->tx_frame_lock); | |
33e6ada1 RG |
923 | } |
924 | isoc_unit = oz_tx_frame_alloc(pd); | |
925 | if (isoc_unit == NULL) | |
926 | goto out; | |
927 | isoc_unit->hdr = oz; | |
928 | isoc_unit->skb = skb; | |
929 | spin_lock_bh(&pd->tx_frame_lock); | |
930 | list_add_tail(&isoc_unit->link, &pd->tx_queue); | |
931 | pd->nb_queued_isoc_frames++; | |
932 | spin_unlock_bh(&pd->tx_frame_lock); | |
f724b584 JP |
933 | oz_dbg(TX_FRAMES, |
934 | "Added ISOC Frame to Tx Queue isoc_nb= %d, nb= %d\n", | |
935 | pd->nb_queued_isoc_frames, pd->nb_queued_frames); | |
33e6ada1 | 936 | return 0; |
bc3157dd | 937 | } |
33e6ada1 RG |
938 | |
939 | /*In ANYTIME mode Xmit unit immediately*/ | |
bc3157dd | 940 | if (atomic_read(&g_submitted_isoc) < OZ_MAX_SUBMITTED_ISOC) { |
bc3157dd | 941 | atomic_inc(&g_submitted_isoc); |
255ece7c | 942 | if (dev_queue_xmit(skb) < 0) |
bc3157dd | 943 | return -1; |
255ece7c | 944 | else |
33e6ada1 | 945 | return 0; |
bc3157dd | 946 | } |
33e6ada1 | 947 | |
255ece7c | 948 | out: kfree_skb(skb); |
33e6ada1 RG |
949 | return -1; |
950 | ||
bc3157dd CK |
951 | } |
952 | return 0; | |
953 | } | |
6e244a83 | 954 | |
4e7fb829 | 955 | /* |
bc3157dd CK |
956 | * Context: process |
957 | */ | |
958 | void oz_apps_init(void) | |
959 | { | |
960 | int i; | |
18f8191e | 961 | |
bc3157dd CK |
962 | for (i = 0; i < OZ_APPID_MAX; i++) |
963 | if (g_app_if[i].init) | |
964 | g_app_if[i].init(); | |
965 | } | |
6e244a83 | 966 | |
4e7fb829 | 967 | /* |
bc3157dd CK |
968 | * Context: process |
969 | */ | |
970 | void oz_apps_term(void) | |
971 | { | |
972 | int i; | |
18f8191e | 973 | |
bc3157dd CK |
974 | /* Terminate all the apps. */ |
975 | for (i = 0; i < OZ_APPID_MAX; i++) | |
976 | if (g_app_if[i].term) | |
977 | g_app_if[i].term(); | |
978 | } | |
6e244a83 | 979 | |
4e7fb829 | 980 | /* |
bc3157dd CK |
981 | * Context: softirq-serialized |
982 | */ | |
983 | void oz_handle_app_elt(struct oz_pd *pd, u8 app_id, struct oz_elt *elt) | |
984 | { | |
dc7f5b35 | 985 | const struct oz_app_if *ai; |
18f8191e | 986 | |
92a62532 | 987 | if (app_id == 0 || app_id > OZ_APPID_MAX) |
bc3157dd CK |
988 | return; |
989 | ai = &g_app_if[app_id-1]; | |
990 | ai->rx(pd, elt); | |
991 | } | |
6e244a83 | 992 | |
4e7fb829 | 993 | /* |
bc3157dd CK |
994 | * Context: softirq or process |
995 | */ | |
996 | void oz_pd_indicate_farewells(struct oz_pd *pd) | |
997 | { | |
998 | struct oz_farewell *f; | |
dc7f5b35 | 999 | const struct oz_app_if *ai = &g_app_if[OZ_APPID_USB-1]; |
18f8191e | 1000 | |
bc3157dd CK |
1001 | while (1) { |
1002 | oz_polling_lock_bh(); | |
1003 | if (list_empty(&pd->farewell_list)) { | |
1004 | oz_polling_unlock_bh(); | |
1005 | break; | |
1006 | } | |
1007 | f = list_first_entry(&pd->farewell_list, | |
1008 | struct oz_farewell, link); | |
1009 | list_del(&f->link); | |
1010 | oz_polling_unlock_bh(); | |
1011 | if (ai->farewell) | |
1012 | ai->farewell(pd, f->ep_num, f->report, f->len); | |
1ec41a31 | 1013 | kfree(f); |
bc3157dd CK |
1014 | } |
1015 | } |