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