[go: nahoru, domu]

ozpd.c revision a7ae725c9295d9076c889bbb75f83cd8e053bfb6
1bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/* -----------------------------------------------------------------------------
2bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Copyright (c) 2011 Ozmo Inc
3bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Released under the GNU General Public License Version 2 (GPLv2).
4bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * -----------------------------------------------------------------------------
5bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
605f608f237dcc3c4e5188d054d9918cbfd2916c2Joe Perches
7bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include <linux/module.h>
8bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include <linux/timer.h>
9bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include <linux/sched.h>
10bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include <linux/netdevice.h>
11072dc1146fb85a9c344138a9751136dff1251333Jérôme Pinot#include <linux/etherdevice.h>
12bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include <linux/errno.h>
13f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches#include "ozdbg.h"
14bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include "ozprotocol.h"
15bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include "ozeltbuf.h"
16bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include "ozpd.h"
17bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include "ozproto.h"
18bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include "ozcdev.h"
19bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include "ozusbsvc.h"
20bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include <asm/unaligned.h>
21bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include <linux/uaccess.h>
22bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#include <net/psnap.h>
2305f608f237dcc3c4e5188d054d9918cbfd2916c2Joe Perches
24bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly#define OZ_MAX_TX_POOL_SIZE	6
25bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
2633e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujarestatic struct oz_tx_frame *oz_tx_frame_alloc(struct oz_pd *pd);
27bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic void oz_tx_frame_free(struct oz_pd *pd, struct oz_tx_frame *f);
28bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic void oz_tx_isoc_free(struct oz_pd *pd, struct oz_tx_frame *f);
29bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic struct sk_buff *oz_build_frame(struct oz_pd *pd, struct oz_tx_frame *f);
30bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic int oz_send_isoc_frame(struct oz_pd *pd);
3133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujarestatic void oz_retire_frame(struct oz_pd *pd, struct oz_tx_frame *f);
32bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic void oz_isoc_stream_free(struct oz_isoc_stream *st);
336e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujarestatic int oz_send_next_queued_frame(struct oz_pd *pd, int more_data);
344e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujarestatic void oz_isoc_destructor(struct sk_buff *skb);
35bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic int oz_def_app_init(void);
36bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic void oz_def_app_term(void);
37bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic int oz_def_app_start(struct oz_pd *pd, int resume);
386e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujarestatic void oz_def_app_stop(struct oz_pd *pd, int pause);
39bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic void oz_def_app_rx(struct oz_pd *pd, struct oz_elt *elt);
40bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
41a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger/*
42a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger * Counts the uncompleted isoc frames submitted to netcard.
43a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger */
44a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaegerstatic atomic_t g_submitted_isoc = ATOMIC_INIT(0);
45a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger
46a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger/* Application handler functions.
47a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger */
48a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaegerstatic const struct oz_app_if g_app_if[OZ_APPID_MAX] = {
49a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	{oz_usb_init,
50a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	oz_usb_term,
51a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	oz_usb_start,
52a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	oz_usb_stop,
53a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	oz_usb_rx,
54a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	oz_usb_heartbeat,
55a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	oz_usb_farewell,
56a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	OZ_APPID_USB},
57a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger
58bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	{oz_def_app_init,
596e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	oz_def_app_term,
606e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	oz_def_app_start,
614e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	oz_def_app_stop,
62bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_def_app_rx,
63bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	NULL,
64bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	NULL,
65bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	OZ_APPID_UNUSED1},
66bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
67bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	{oz_def_app_init,
68bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_def_app_term,
69f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	oz_def_app_start,
70bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_def_app_stop,
71bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_def_app_rx,
72f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	NULL,
73bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	NULL,
74bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	OZ_APPID_UNUSED2},
75f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches
76bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	{oz_cdev_init,
77bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_cdev_term,
78f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	oz_cdev_start,
79bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_cdev_stop,
80bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_cdev_rx,
81bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	NULL,
826e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	NULL,
834e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	OZ_APPID_SERIAL},
84bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly};
85bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
86bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
87bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: process
88bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
89bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic int oz_def_app_init(void)
906e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare{
914e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	return 0;
92bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
93bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
94bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
95bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: process
96bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
97bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic void oz_def_app_term(void)
98bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
996e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare}
1004e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare
101bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
102bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq
103dc7f5b3594fdb846890192fd75793a791d7ba83bPeter Huewe */
104bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic int oz_def_app_start(struct oz_pd *pd, int resume)
1051ec41a31fb695682cab7fc7c1f6ced84d188b6f9Greg Kroah-Hartman{
10618f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare	return 0;
107bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
108bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
109bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
110a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger * Context: softirq
111bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
112bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic void oz_def_app_stop(struct oz_pd *pd, int pause)
113bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
114bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
115072dc1146fb85a9c344138a9751136dff1251333Jérôme Pinot
116a7ae725c9295d9076c889bbb75f83cd8e053bfb6Christoph Jaeger/*
117bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq
118bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
119bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic void oz_def_app_rx(struct oz_pd *pd, struct oz_elt *elt)
120bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
121bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
122bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
1238fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare/*
1248fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare * Context: softirq or process
1258fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare */
1268fd070077410b26847ef6f5856850df417e7b83eRupesh Gujarevoid oz_pd_set_state(struct oz_pd *pd, unsigned state)
1278fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare{
1288fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare	pd->state = state;
1298fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare	switch (state) {
1308fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare	case OZ_PD_S_IDLE:
131bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_IDLE\n");
132bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		break;
133bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	case OZ_PD_S_CONNECTED:
1346e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare		oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_CONNECTED\n");
1354e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare		break;
136bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	case OZ_PD_S_STOPPED:
137bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_STOPPED\n");
138421acbc251a016b98797a06103374b48b30f943aFengguang Wu		break;
139bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	case OZ_PD_S_SLEEP:
140bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_SLEEP\n");
141bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		break;
142bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
143bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
1446af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare
14518f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare/*
146f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches * Context: softirq or process
1476af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare */
1488fd070077410b26847ef6f5856850df417e7b83eRupesh Gujarevoid oz_pd_get(struct oz_pd *pd)
1498fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare{
1508fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare	atomic_inc(&pd->ref_count);
151bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
152bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
153bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
154bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq or process
155bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
156bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellyvoid oz_pd_put(struct oz_pd *pd)
157bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
158bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (atomic_dec_and_test(&pd->ref_count))
159bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_pd_destroy(pd);
160bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
161bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
162bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
163bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq-serialized
164bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
16533e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujarestruct oz_pd *oz_pd_alloc(const u8 *mac_addr)
166dd3cef0f0715b79d29a4b0482117175f44b7d83cGreg Kroah-Hartman{
167bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct oz_pd *pd = kzalloc(sizeof(struct oz_pd), GFP_ATOMIC);
168bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
169bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (pd) {
170bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		int i;
171bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		atomic_set(&pd->ref_count, 2);
172bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		for (i = 0; i < OZ_APPID_MAX; i++)
173bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			spin_lock_init(&pd->app_lock[i]);
174bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		pd->last_rx_pkt_num = 0xffffffff;
175bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_pd_set_state(pd, OZ_PD_S_IDLE);
1761ec41a31fb695682cab7fc7c1f6ced84d188b6f9Greg Kroah-Hartman		pd->max_tx_size = OZ_MAX_TX_SIZE;
177bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		ether_addr_copy(pd->mac_addr, mac_addr);
178bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_elt_buf_init(&pd->elt_buff);
179bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		spin_lock_init(&pd->tx_frame_lock);
1801ec41a31fb695682cab7fc7c1f6ced84d188b6f9Greg Kroah-Hartman		INIT_LIST_HEAD(&pd->tx_queue);
181bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		INIT_LIST_HEAD(&pd->farewell_list);
1826e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare		pd->last_sent_frame = &pd->tx_queue;
1834e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare		spin_lock_init(&pd->stream_lock);
1846af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare		INIT_LIST_HEAD(&pd->stream_list);
1856af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare		tasklet_init(&pd->heartbeat_tasklet, oz_pd_heartbeat_handler,
1866af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare							(unsigned long)pd);
1876af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare		tasklet_init(&pd->timeout_tasklet, oz_pd_timeout_handler,
1886af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare							(unsigned long)pd);
1896af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare		hrtimer_init(&pd->heartbeat, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
1906af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare		hrtimer_init(&pd->timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
1916af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare		pd->heartbeat.function = oz_pd_heartbeat_event;
1926af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare		pd->timeout.function = oz_pd_timeout_event;
1936af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare	}
194dfc065f19fe0ce4ef7a52011801c5c19131e6529Rupesh Gujare	return pd;
1956af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare}
1966af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare
1976af47622398d65e4cb3a52af5de52c6b447270d5Rupesh Gujare/*
1984e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare * Context: softirq or process
199bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
200bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic void oz_pd_free(struct work_struct *work)
201bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
202bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct list_head *e;
203a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	struct oz_tx_frame *f;
20418f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare	struct oz_isoc_stream *st;
205f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	struct oz_farewell *fwell;
206a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	struct oz_pd *pd;
207a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger
208a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	oz_pd_dbg(pd, ON, "Destroying PD\n");
209bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	pd = container_of(work, struct oz_pd, workitem);
210f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	/*Disable timer tasklets*/
211a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	tasklet_kill(&pd->heartbeat_tasklet);
212bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	tasklet_kill(&pd->timeout_tasklet);
213bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	/* Delete any streams.
2146b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil	 */
215a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	e = pd->stream_list.next;
216bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	while (e != &pd->stream_list) {
217a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger		st = container_of(e, struct oz_isoc_stream, link);
2186b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil		e = e->next;
219bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_isoc_stream_free(st);
220bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
221bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	/* Free any queued tx frames.
222bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	 */
2236e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	e = pd->tx_queue.next;
2244e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	while (e != &pd->tx_queue) {
225bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		f = container_of(e, struct oz_tx_frame, link);
226bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		e = e->next;
227bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		if (f->skb != NULL)
228bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			kfree_skb(f->skb);
229a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger		oz_retire_frame(pd, f);
23018f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare	}
231f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	oz_elt_buf_term(&pd->elt_buff);
232a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	/* Free any farewells.
233a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	 */
2346b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil	e = pd->farewell_list.next;
235bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	while (e != &pd->farewell_list) {
236a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger		fwell = container_of(e, struct oz_farewell, link);
237bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		e = e->next;
238a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger		kfree(fwell);
239a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	}
240bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	/* Deallocate all frames in tx pool.
2416b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil	 */
242a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger	while (pd->tx_pool) {
243bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		e = pd->tx_pool;
244bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		pd->tx_pool = e->next;
245bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		kfree(container_of(e, struct oz_tx_frame, link));
2466e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	}
2474e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	if (pd->net_dev)
248bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		dev_put(pd->net_dev);
249bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	kfree(pd);
250bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
251bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
252a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger/*
25318f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare * Context: softirq or Process
254a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger */
255a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaegervoid oz_pd_destroy(struct oz_pd *pd)
256a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger{
257bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (hrtimer_active(&pd->timeout))
258bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		hrtimer_cancel(&pd->timeout);
259bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (hrtimer_active(&pd->heartbeat))
2608fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare		hrtimer_cancel(&pd->heartbeat);
2618fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare
262bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	INIT_WORK(&pd->workitem, oz_pd_free);
263bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (!schedule_work(&pd->workitem))
264bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_pd_dbg(pd, ON, "failed to schedule workitem\n");
265bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
266bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
267bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
2686e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare * Context: softirq-serialized
2694e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare */
270bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellyint oz_services_start(struct oz_pd *pd, u16 apps, int resume)
271bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
272bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	const struct oz_app_if *ai;
273bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	int rc = 0;
274a15e042e262bb62cd8dd9a7ccda9137c9e65cedcRupesh Gujare
27518f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare	oz_pd_dbg(pd, ON, "%s: (0x%x) resume(%d)\n", __func__, apps, resume);
276f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	for (ai = g_app_if; ai < &g_app_if[OZ_APPID_MAX]; ai++) {
277bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		if (apps & (1<<ai->app_id)) {
2786b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil			if (ai->start(pd, resume)) {
279bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				rc = -1;
280bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				oz_pd_dbg(pd, ON,
281bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly					  "Unable to start service %d\n",
2826b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil					  ai->app_id);
283bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				break;
2846b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil			}
285bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			spin_lock_bh(&g_polling_lock);
286bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			pd->total_apps |= (1<<ai->app_id);
287bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			if (resume)
2886b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil				pd->paused_apps &= ~(1<<ai->app_id);
289f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches			spin_unlock_bh(&g_polling_lock);
290bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		}
291bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
2926e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	return rc;
2934e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare}
294bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
295bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
296bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq or process
297bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
298bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellyvoid oz_services_stop(struct oz_pd *pd, u16 apps, int pause)
299a15e042e262bb62cd8dd9a7ccda9137c9e65cedcRupesh Gujare{
30018f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare	const struct oz_app_if *ai;
3016b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil
302bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_pd_dbg(pd, ON, "%s: (0x%x) pause(%d)\n", __func__, apps, pause);
3036b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil	for (ai = g_app_if; ai < &g_app_if[OZ_APPID_MAX]; ai++) {
304bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		if (apps & (1<<ai->app_id)) {
305bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			spin_lock_bh(&g_polling_lock);
3068fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare			if (pause) {
307bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				pd->paused_apps |= (1<<ai->app_id);
3088fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare			} else {
309bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				pd->total_apps &= ~(1<<ai->app_id);
3108fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare				pd->paused_apps &= ~(1<<ai->app_id);
311bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			}
3126b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil			spin_unlock_bh(&g_polling_lock);
313bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			ai->stop(pd, pause);
314bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		}
315bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
316bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
3178fd070077410b26847ef6f5856850df417e7b83eRupesh Gujare
318bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
319bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq
320bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
3216e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujarevoid oz_pd_heartbeat(struct oz_pd *pd, u16 apps)
3224e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare{
323bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	const struct oz_app_if *ai;
324bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	int more = 0;
325bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
326bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	for (ai = g_app_if; ai < &g_app_if[OZ_APPID_MAX]; ai++) {
32750222db4b03ac8f3259c6d39bbd585ed3358f70fChristoph Jaeger		if (ai->heartbeat && (apps & (1<<ai->app_id))) {
32818f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare			if (ai->heartbeat(pd))
32950222db4b03ac8f3259c6d39bbd585ed3358f70fChristoph Jaeger				more = 1;
330bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		}
331bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
332bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if ((!more) && (hrtimer_active(&pd->heartbeat)))
333bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		hrtimer_cancel(&pd->heartbeat);
334bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (pd->mode & OZ_F_ISOC_ANYTIME) {
335bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		int count = 8;
336bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		while (count-- && (oz_send_isoc_frame(pd) >= 0))
3376e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare			;
3384e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	}
339bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
340bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
34133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare/*
34233e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare * Context: softirq or process
34333e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare */
34433e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujarevoid oz_pd_stop(struct oz_pd *pd)
34550222db4b03ac8f3259c6d39bbd585ed3358f70fChristoph Jaeger{
34650222db4b03ac8f3259c6d39bbd585ed3358f70fChristoph Jaeger	u16 stop_apps;
34750222db4b03ac8f3259c6d39bbd585ed3358f70fChristoph Jaeger
348f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	oz_dbg(ON, "oz_pd_stop() State = 0x%x\n", pd->state);
349f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	oz_pd_indicate_farewells(pd);
35033e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	spin_lock_bh(&g_polling_lock);
3516e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	stop_apps = pd->total_apps;
3524e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	pd->total_apps = 0;
35333e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	pd->paused_apps = 0;
35433e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	spin_unlock_bh(&g_polling_lock);
355bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_services_stop(pd, stop_apps, 0);
356bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	spin_lock_bh(&g_polling_lock);
35750222db4b03ac8f3259c6d39bbd585ed3358f70fChristoph Jaeger	oz_pd_set_state(pd, OZ_PD_S_STOPPED);
358bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	/* Remove from PD list.*/
3596e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	list_del(&pd->link);
3604e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	spin_unlock_bh(&g_polling_lock);
36133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	oz_dbg(ON, "pd ref count = %d\n", atomic_read(&pd->ref_count));
36233e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	oz_pd_put(pd);
363a7f74c3005cf6830a20d71c4e1d8d6d77443e5cbPeter Huewe}
36433e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare
36533e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare/*
36618f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare * Context: softirq
36733e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare */
36833e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujareint oz_pd_sleep(struct oz_pd *pd)
3696e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare{
3704e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	int do_stop = 0;
37100ec12b827d2f99202e0c9d804514aef2febf8d6Rupesh Gujare	u16 stop_apps;
37200ec12b827d2f99202e0c9d804514aef2febf8d6Rupesh Gujare
373a7f74c3005cf6830a20d71c4e1d8d6d77443e5cbPeter Huewe	spin_lock_bh(&g_polling_lock);
37400ec12b827d2f99202e0c9d804514aef2febf8d6Rupesh Gujare	if (pd->state & (OZ_PD_S_SLEEP | OZ_PD_S_STOPPED)) {
37500ec12b827d2f99202e0c9d804514aef2febf8d6Rupesh Gujare		spin_unlock_bh(&g_polling_lock);
37618f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare		return 0;
37700ec12b827d2f99202e0c9d804514aef2febf8d6Rupesh Gujare	}
37800ec12b827d2f99202e0c9d804514aef2febf8d6Rupesh Gujare	if (pd->keep_alive && pd->session_id)
3796e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare		oz_pd_set_state(pd, OZ_PD_S_SLEEP);
3804e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	else
381bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		do_stop = 1;
382bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
383bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	stop_apps = pd->total_apps;
384bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	spin_unlock_bh(&g_polling_lock);
385bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (do_stop) {
38618f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare		oz_pd_stop(pd);
387bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	} else {
388bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_services_stop(pd, stop_apps, 1);
389bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_timer_add(pd, OZ_TIMER_STOP, pd->keep_alive);
390bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
391bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	return do_stop;
392bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
393bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
39486b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe/*
395bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq
39633e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare */
397bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic struct oz_tx_frame *oz_tx_frame_alloc(struct oz_pd *pd)
398bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
399bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct oz_tx_frame *f = NULL;
400bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
401bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	spin_lock_bh(&pd->tx_frame_lock);
402bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (pd->tx_pool) {
403bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		f = container_of(pd->tx_pool, struct oz_tx_frame, link);
404bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		pd->tx_pool = pd->tx_pool->next;
405bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		pd->tx_pool_count--;
406bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
407bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	spin_unlock_bh(&pd->tx_frame_lock);
408bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (f == NULL)
409bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		f = kmalloc(sizeof(struct oz_tx_frame), GFP_ATOMIC);
410bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (f) {
4116e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare		f->total_size = sizeof(struct oz_hdr);
4124e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare		INIT_LIST_HEAD(&f->link);
413bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		INIT_LIST_HEAD(&f->elt_list);
414bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
415bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	return f;
416bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
41786b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe
418bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
419bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq or process
420bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
421bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic void oz_tx_isoc_free(struct oz_pd *pd, struct oz_tx_frame *f)
42218f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare{
423bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	pd->nb_queued_isoc_frames--;
424bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	list_del_init(&f->link);
425bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (pd->tx_pool_count < OZ_MAX_TX_POOL_SIZE) {
426ec0ee957f657b6200a2629d4c54df68d2c622e12Greg Kroah-Hartman		f->link.next = pd->tx_pool;
42786b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe		pd->tx_pool = &f->link;
42886b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe		pd->tx_pool_count++;
429bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	} else {
430bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		kfree(f);
431bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
432bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_dbg(TX_FRAMES, "Releasing ISOC Frame isoc_nb= %d\n",
433bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	       pd->nb_queued_isoc_frames);
434bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
435bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
436bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
437bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq or process
438bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
439bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic void oz_tx_frame_free(struct oz_pd *pd, struct oz_tx_frame *f)
440bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
441bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	spin_lock_bh(&pd->tx_frame_lock);
442bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (pd->tx_pool_count < OZ_MAX_TX_POOL_SIZE) {
443bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		f->link.next = pd->tx_pool;
444bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		pd->tx_pool = &f->link;
445bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		pd->tx_pool_count++;
446bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		f = NULL;
447bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
448bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	spin_unlock_bh(&pd->tx_frame_lock);
449bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	kfree(f);
450bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
451bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
452bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
453bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq-serialized
454dd3cef0f0715b79d29a4b0482117175f44b7d83cGreg Kroah-Hartman */
45586b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewestatic void oz_set_more_bit(struct sk_buff *skb)
456bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
4576e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb);
4584e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare
459bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_hdr->control |= OZ_F_MORE_DATA;
460bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
461bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
462bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
463bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq-serialized
464bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
46518f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujarestatic void oz_set_last_pkt_nb(struct oz_pd *pd, struct sk_buff *skb)
466bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
467bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb);
468bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
469bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_hdr->last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK;
470bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
471bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
472bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
473bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq
474bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
475bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellyint oz_prepare_frame(struct oz_pd *pd, int empty)
476bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
477bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct oz_tx_frame *f;
478bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
4796e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	if ((pd->mode & OZ_MODE_MASK) != OZ_MODE_TRIGGERED)
4804e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare		return -1;
481bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (pd->nb_queued_frames >= OZ_MAX_QUEUED_FRAMES)
482bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		return -1;
48333e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	if (!empty && !oz_are_elts_available(&pd->elt_buff))
484bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		return -1;
485bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	f = oz_tx_frame_alloc(pd);
486bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (f == NULL)
487bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		return -1;
48818f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare	f->skb = NULL;
489bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	f->hdr.control =
490bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		(OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT) | OZ_F_ACK_REQUESTED;
491bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	++pd->last_tx_pkt_num;
492bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	put_unaligned(cpu_to_le32(pd->last_tx_pkt_num), &f->hdr.pkt_num);
493bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (empty == 0) {
494bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_select_elts_for_tx(&pd->elt_buff, 0, &f->total_size,
495bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			pd->max_tx_size, &f->elt_list);
49633e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	}
49733e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	spin_lock(&pd->tx_frame_lock);
49833e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	list_add_tail(&f->link, &pd->tx_queue);
49933e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	pd->nb_queued_frames++;
50033e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	spin_unlock(&pd->tx_frame_lock);
50133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	return 0;
50233e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare}
50300ec12b827d2f99202e0c9d804514aef2febf8d6Rupesh Gujare
50433e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare/*
50533e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare * Context: softirq-serialized
50633e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare */
507f724b5843431aba591a01b6988a725689cd9ebb3Joe Perchesstatic struct sk_buff *oz_build_frame(struct oz_pd *pd, struct oz_tx_frame *f)
50833e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare{
50933e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	struct sk_buff *skb;
51033e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	struct net_device *dev = pd->net_dev;
511f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	struct oz_hdr *oz_hdr;
512f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	struct oz_elt *elt;
51333e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	struct list_head *e;
51433e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare
515dd3cef0f0715b79d29a4b0482117175f44b7d83cGreg Kroah-Hartman	/* Allocate skb with enough space for the lower layers as well
516f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	 * as the space we need.
51733e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	 */
51833e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	skb = alloc_skb(f->total_size + OZ_ALLOCATED_SPACE(dev), GFP_ATOMIC);
51933e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	if (skb == NULL)
52033e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		return NULL;
52133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	/* Reserve the head room for lower layers.
522bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	 */
523bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	skb_reserve(skb, LL_RESERVED_SPACE(dev));
52437bc8f7856fc0c851980f01174ca936741511523Rupesh Gujare	skb_reset_network_header(skb);
52537bc8f7856fc0c851980f01174ca936741511523Rupesh Gujare	skb->dev = dev;
52633e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	skb->protocol = htons(OZ_ETHERTYPE);
52733e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr,
528f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches		dev->dev_addr, skb->len) < 0)
52937bc8f7856fc0c851980f01174ca936741511523Rupesh Gujare		goto fail;
53037bc8f7856fc0c851980f01174ca936741511523Rupesh Gujare	/* Push the tail to the end of the area we are going to copy to.
53133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	 */
532bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_hdr = (struct oz_hdr *)skb_put(skb, f->total_size);
533bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	f->hdr.last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK;
5346e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	memcpy(oz_hdr, &f->hdr, sizeof(struct oz_hdr));
5354e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	/* Copy the elements into the frame body.
536bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	 */
537bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	elt = (struct oz_elt *)(oz_hdr+1);
538bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	for (e = f->elt_list.next; e != &f->elt_list; e = e->next) {
539bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		struct oz_elt_info *ei;
54033e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		ei = container_of(e, struct oz_elt_info, link);
54133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		memcpy(elt, ei->data, ei->length);
54233e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		elt = oz_next_elt(elt);
54333e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	}
54433e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	return skb;
54533e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujarefail:
54633e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	kfree_skb(skb);
54733e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	return NULL;
54833e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare}
54933e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare
55033e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare/*
55133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare * Context: softirq or process
552bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
55333e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujarestatic void oz_retire_frame(struct oz_pd *pd, struct oz_tx_frame *f)
55433e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare{
55533e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	struct list_head *e;
55633e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	struct oz_elt_info *ei;
55733e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare
55833e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	e = f->elt_list.next;
55933e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	while (e != &f->elt_list) {
56033e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		ei = container_of(e, struct oz_elt_info, link);
56133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		e = e->next;
56233e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		list_del_init(&ei->link);
56333e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		if (ei->callback)
56433e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare			ei->callback(pd, ei->context);
56533e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		spin_lock_bh(&pd->elt_buff.lock);
56633e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		oz_elt_info_free(&pd->elt_buff, ei);
567bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		spin_unlock_bh(&pd->elt_buff.lock);
56833e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	}
56933e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	oz_tx_frame_free(pd, f);
57033e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	if (pd->elt_buff.free_elts > pd->elt_buff.max_free_elts)
57133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		oz_trim_elt_pool(&pd->elt_buff);
572bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
5736e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare
5744e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare/*
575bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq-serialized
576bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
577bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic int oz_send_next_queued_frame(struct oz_pd *pd, int more_data)
578bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
57986b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe	struct sk_buff *skb;
580bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct oz_tx_frame *f;
581bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct list_head *e;
582bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
583bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	spin_lock(&pd->tx_frame_lock);
584bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	e = pd->last_sent_frame->next;
585bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (e == &pd->tx_queue) {
58618f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare		spin_unlock(&pd->tx_frame_lock);
587bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		return -1;
588bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
589bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	f = container_of(e, struct oz_tx_frame, link);
590bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
591bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (f->skb != NULL) {
592bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		skb = f->skb;
593ec0ee957f657b6200a2629d4c54df68d2c622e12Greg Kroah-Hartman		oz_tx_isoc_free(pd, f);
59486b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe		spin_unlock(&pd->tx_frame_lock);
595f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches		if (more_data)
596bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			oz_set_more_bit(skb);
597bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_set_last_pkt_nb(pd, skb);
598bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		if ((int)atomic_read(&g_submitted_isoc) <
599bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly							OZ_MAX_SUBMITTED_ISOC) {
600bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			if (dev_queue_xmit(skb) < 0) {
601bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				oz_dbg(TX_FRAMES, "Dropping ISOC Frame\n");
602bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				return -1;
603bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			}
604bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			atomic_inc(&g_submitted_isoc);
605dd3cef0f0715b79d29a4b0482117175f44b7d83cGreg Kroah-Hartman			oz_dbg(TX_FRAMES, "Sending ISOC Frame, nb_isoc= %d\n",
606bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			       pd->nb_queued_isoc_frames);
607bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			return 0;
608bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		} else {
609bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			kfree_skb(skb);
610bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			oz_dbg(TX_FRAMES, "Dropping ISOC Frame>\n");
611bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			return -1;
612bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		}
613bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
614bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
615bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	pd->last_sent_frame = e;
616bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	skb = oz_build_frame(pd, f);
617bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	spin_unlock(&pd->tx_frame_lock);
618bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (!skb)
619bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		return -1;
620bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (more_data)
621bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_set_more_bit(skb);
622bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_dbg(TX_FRAMES, "TX frame PN=0x%x\n", f->hdr.pkt_num);
6236e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	if (dev_queue_xmit(skb) < 0)
6244e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare		return -1;
625bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
626bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	return 0;
627bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
628bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
629bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
630bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq-serialized
63186b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe */
63286b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewevoid oz_send_queued_frames(struct oz_pd *pd, int backlog)
633bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
634bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	while (oz_prepare_frame(pd, 0) >= 0)
635bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		backlog++;
636bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
637bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	switch (pd->mode & (OZ_F_ISOC_NO_ELTS | OZ_F_ISOC_ANYTIME)) {
638bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
639bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		case OZ_F_ISOC_NO_ELTS: {
640bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			backlog += pd->nb_queued_isoc_frames;
641bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			if (backlog <= 0)
64233e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare				goto out;
643bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			if (backlog > OZ_MAX_SUBMITTED_ISOC)
644f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches				backlog = OZ_MAX_SUBMITTED_ISOC;
645f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches			break;
64686b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe		}
647bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		case OZ_NO_ELTS_ANYTIME: {
648bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			if ((backlog <= 0) && (pd->isoc_sent == 0))
649bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				goto out;
650bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			break;
651bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		}
652bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		default: {
653bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			if (backlog <= 0)
654bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				goto out;
65586b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe			break;
656bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		}
657bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
658bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	while (backlog--) {
659bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		if (oz_send_next_queued_frame(pd, backlog) < 0)
660bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			break;
661bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
662bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	return;
663bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
664bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellyout:	oz_prepare_frame(pd, 1);
6656e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	oz_send_next_queued_frame(pd, 0);
6664e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare}
667bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
668bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
669bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq
670bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
671bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic int oz_send_isoc_frame(struct oz_pd *pd)
672bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
673bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct sk_buff *skb;
67418f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare	struct net_device *dev = pd->net_dev;
675bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct oz_hdr *oz_hdr;
676bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct oz_elt *elt;
677bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct list_head *e;
678bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct list_head list;
679bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	int total_size = sizeof(struct oz_hdr);
68086b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe
681bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	INIT_LIST_HEAD(&list);
6826e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare
6834e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	oz_select_elts_for_tx(&pd->elt_buff, 1, &total_size,
684bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		pd->max_tx_size, &list);
685bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (list.next == &list)
686bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		return 0;
687bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	skb = alloc_skb(total_size + OZ_ALLOCATED_SPACE(dev), GFP_ATOMIC);
688bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (skb == NULL) {
6891ec41a31fb695682cab7fc7c1f6ced84d188b6f9Greg Kroah-Hartman		oz_dbg(ON, "Cannot alloc skb\n");
690bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_elt_info_free_chain(&pd->elt_buff, &list);
6911ec41a31fb695682cab7fc7c1f6ced84d188b6f9Greg Kroah-Hartman		return -1;
692bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
693bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	skb_reserve(skb, LL_RESERVED_SPACE(dev));
694bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	skb_reset_network_header(skb);
695bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	skb->dev = dev;
69686b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe	skb->protocol = htons(OZ_ETHERTYPE);
697bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr,
698bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		dev->dev_addr, skb->len) < 0) {
699b150718ecb6468d2de1ff593c402ec257dd80c8cSachin Kamat		kfree_skb(skb);
700bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		return -1;
701bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
7026e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	oz_hdr = (struct oz_hdr *)skb_put(skb, total_size);
7034e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	oz_hdr->control = (OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT) | OZ_F_ISOC;
704bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_hdr->last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK;
705bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	elt = (struct oz_elt *)(oz_hdr+1);
706bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
707bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	for (e = list.next; e != &list; e = e->next) {
708dd3cef0f0715b79d29a4b0482117175f44b7d83cGreg Kroah-Hartman		struct oz_elt_info *ei;
7091ec41a31fb695682cab7fc7c1f6ced84d188b6f9Greg Kroah-Hartman		ei = container_of(e, struct oz_elt_info, link);
710bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		memcpy(elt, ei->data, ei->length);
7116e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare		elt = oz_next_elt(elt);
7124e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	}
713bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	dev_queue_xmit(skb);
714bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	oz_elt_info_free_chain(&pd->elt_buff, &list);
715bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	return 0;
716bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
717bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
71818f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare/*
719bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq-serialized
720bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
721bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellyvoid oz_retire_tx_frames(struct oz_pd *pd, u8 lpn)
722bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
723bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct list_head *e;
724bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct oz_tx_frame *f;
725bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct list_head *first = NULL;
726bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct list_head *last = NULL;
727bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	u8 diff;
7286e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	u32 pkt_num;
7294e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare
730bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	spin_lock(&pd->tx_frame_lock);
731bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	e = pd->tx_queue.next;
732bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	while (e != &pd->tx_queue) {
733bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		f = container_of(e, struct oz_tx_frame, link);
734bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		pkt_num = le32_to_cpu(get_unaligned(&f->hdr.pkt_num));
735bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		diff = (lpn - (pkt_num & OZ_LAST_PN_MASK)) & OZ_LAST_PN_MASK;
7366e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare		if ((diff > OZ_LAST_PN_HALF_CYCLE) || (pkt_num == 0))
7374e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare			break;
738bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_dbg(TX_FRAMES, "Releasing pkt_num= %u, nb= %d\n",
739bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		       pkt_num, pd->nb_queued_frames);
740dc7f5b3594fdb846890192fd75793a791d7ba83bPeter Huewe		if (first == NULL)
741bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			first = e;
742bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		last = e;
743bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		e = e->next;
744bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		pd->nb_queued_frames--;
74586b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe	}
74686b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe	if (first) {
747bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		last->next->prev = &pd->tx_queue;
74818f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare		pd->tx_queue.next = last->next;
749bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		last->next = NULL;
750bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
751bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	pd->last_sent_frame = &pd->tx_queue;
752bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	spin_unlock(&pd->tx_frame_lock);
75386b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe	while (first) {
754bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		f = container_of(first, struct oz_tx_frame, link);
755bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		first = first->next;
756bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_retire_frame(pd, f);
757bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
758bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
759bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
760bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
761bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Precondition: stream_lock must be held.
762bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq
763bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
764ec0ee957f657b6200a2629d4c54df68d2c622e12Greg Kroah-Hartmanstatic struct oz_isoc_stream *pd_stream_find(struct oz_pd *pd, u8 ep_num)
765ec0ee957f657b6200a2629d4c54df68d2c622e12Greg Kroah-Hartman{
76686b02be043c42e1d7450be025177e3e248a2e8c1Peter Huewe	struct list_head *e;
767bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct oz_isoc_stream *st;
768bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
769bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	list_for_each(e, &pd->stream_list) {
770bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		st = container_of(e, struct oz_isoc_stream, link);
771bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		if (st->ep_num == ep_num)
772bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			return st;
77386c948b4416b4c2c064d6c7e15476afbc04e2bf5Rupesh Gujare	}
77486c948b4416b4c2c064d6c7e15476afbc04e2bf5Rupesh Gujare	return NULL;
775bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
776bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
777bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
778bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq
779bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
780bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellyint oz_isoc_stream_create(struct oz_pd *pd, u8 ep_num)
781bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
782bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	struct oz_isoc_stream *st =
783bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		kzalloc(sizeof(struct oz_isoc_stream), GFP_ATOMIC);
784bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (!st)
785bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		return -ENOMEM;
786bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	st->ep_num = ep_num;
787bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	spin_lock_bh(&pd->stream_lock);
788bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (!pd_stream_find(pd, ep_num)) {
789bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		list_add(&st->link, &pd->stream_list);
790bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		st = NULL;
791bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	}
792bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	spin_unlock_bh(&pd->stream_lock);
793bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	kfree(st);
794bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	return 0;
795bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly}
796bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
797bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly/*
798bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly * Context: softirq or process
799bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly */
800bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kellystatic void oz_isoc_stream_free(struct oz_isoc_stream *st)
801bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly{
802bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	kfree_skb(st->skb);
803bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	kfree(st);
80433e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare}
80533e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare
80633e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare/*
80733e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare * Context: softirq
80833e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare */
80933e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujareint oz_isoc_stream_delete(struct oz_pd *pd, u8 ep_num)
81033e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare{
81133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	struct oz_isoc_stream *st;
81286d03a0f4f575dda7988800a3da8d6e9f776a819Rupesh Gujare
8132dc2ee5ccdaf88b41bf9c025acf14ebdc2959a21Rupesh Gujare	spin_lock_bh(&pd->stream_lock);
8142dc2ee5ccdaf88b41bf9c025acf14ebdc2959a21Rupesh Gujare	st = pd_stream_find(pd, ep_num);
815f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	if (st)
816f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches		list_del(&st->link);
8172dc2ee5ccdaf88b41bf9c025acf14ebdc2959a21Rupesh Gujare	spin_unlock_bh(&pd->stream_lock);
8182dc2ee5ccdaf88b41bf9c025acf14ebdc2959a21Rupesh Gujare	if (st)
8192dc2ee5ccdaf88b41bf9c025acf14ebdc2959a21Rupesh Gujare		oz_isoc_stream_free(st);
8202dc2ee5ccdaf88b41bf9c025acf14ebdc2959a21Rupesh Gujare	return 0;
8212dc2ee5ccdaf88b41bf9c025acf14ebdc2959a21Rupesh Gujare}
8222dc2ee5ccdaf88b41bf9c025acf14ebdc2959a21Rupesh Gujare
8232dc2ee5ccdaf88b41bf9c025acf14ebdc2959a21Rupesh Gujare/*
8242dc2ee5ccdaf88b41bf9c025acf14ebdc2959a21Rupesh Gujare * Context: any
8252dc2ee5ccdaf88b41bf9c025acf14ebdc2959a21Rupesh Gujare */
8262dc2ee5ccdaf88b41bf9c025acf14ebdc2959a21Rupesh Gujarestatic void oz_isoc_destructor(struct sk_buff *skb)
82733e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare{
82833e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	atomic_dec(&g_submitted_isoc);
82933e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare}
83033e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare
83133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare/*
83233e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare * Context: softirq
83333e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare */
83433e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujareint oz_send_isoc_unit(struct oz_pd *pd, u8 ep_num, const u8 *data, int len)
83533e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare{
83633e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	struct net_device *dev = pd->net_dev;
837f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	struct oz_isoc_stream *st;
838f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	u8 nb_units = 0;
839f724b5843431aba591a01b6988a725689cd9ebb3Joe Perches	struct sk_buff *skb = NULL;
84033e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	struct oz_hdr *oz_hdr = NULL;
841bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	int size = 0;
84233e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare
84333e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	spin_lock_bh(&pd->stream_lock);
844bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	st = pd_stream_find(pd, ep_num);
845bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (st) {
846255ece7c4d9e63e2a5e784247bc2e7b639cae4ddRupesh Gujare		skb = st->skb;
847bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		st->skb = NULL;
848255ece7c4d9e63e2a5e784247bc2e7b639cae4ddRupesh Gujare		nb_units = st->nb_units;
84933e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		st->nb_units = 0;
850bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_hdr = st->oz_hdr;
85133e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare		size = st->size;
852255ece7c4d9e63e2a5e784247bc2e7b639cae4ddRupesh Gujare	}
85333e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	spin_unlock_bh(&pd->stream_lock);
85433e6ada17fffc54c24607d5acb279363b30ac401Rupesh Gujare	if (!st)
855bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		return 0;
856bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (!skb) {
857bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		/* Allocate enough space for max size frame. */
8586e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare		skb = alloc_skb(pd->max_tx_size + OZ_ALLOCATED_SPACE(dev),
8594e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare				GFP_ATOMIC);
860bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		if (skb == NULL)
861bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			return 0;
862bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		/* Reserve the head room for lower layers. */
863bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		skb_reserve(skb, LL_RESERVED_SPACE(dev));
864bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		skb_reset_network_header(skb);
86518f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare		skb->dev = dev;
866a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger		skb->protocol = htons(OZ_ETHERTYPE);
867bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		/* For audio packet set priority to AC_VO */
868bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		skb->priority = 0x7;
869a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger		size = sizeof(struct oz_hdr) + sizeof(struct oz_isoc_large);
870bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz_hdr = (struct oz_hdr *)skb_put(skb, size);
8716e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare	}
8724e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare	memcpy(skb_put(skb, len), data, len);
873bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	size += len;
874bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	if (++nb_units < pd->ms_per_isoc) {
875bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		spin_lock_bh(&pd->stream_lock);
876bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		st->skb = skb;
877bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		st->nb_units = nb_units;
87818f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare		st->oz_hdr = oz_hdr;
879bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		st->size = size;
880a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger		spin_unlock_bh(&pd->stream_lock);
881bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly	} else {
882bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		struct oz_hdr oz;
883a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger		struct oz_isoc_large iso;
884bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		spin_lock_bh(&pd->stream_lock);
8856e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare		iso.frame_number = st->frame_num;
8864e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare		st->frame_num += nb_units;
887bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		spin_unlock_bh(&pd->stream_lock);
888bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz.control =
889bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			(OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT) | OZ_F_ISOC;
890bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		oz.last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK;
891a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger		oz.pkt_num = 0;
892a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger		iso.endpoint = ep_num;
893bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		iso.format = OZ_DATA_F_ISOC_LARGE;
8946e244a8319ab80acdda424795e85687fb6af0be2Rupesh Gujare		iso.ms_data = nb_units;
8954e7fb829771de2cf8a5ba9576290d76936d3e814Rupesh Gujare		memcpy(oz_hdr, &oz, sizeof(oz));
896bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		memcpy(oz_hdr+1, &iso, sizeof(iso));
897bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr,
898bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				dev->dev_addr, skb->len) < 0)
899bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			goto out;
900bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly
901a9686e786896297f9f1d74a2cac4ffccc7b3e50eChristoph Jaeger		skb->destructor = oz_isoc_destructor;
90218f8191e1f35d65221ad3b7c68c08fe1c09edb1dRupesh Gujare		/*Queue for Xmit if mode is not ANYTIME*/
903bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly		if (!(pd->mode & OZ_F_ISOC_ANYTIME)) {
9046b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil			struct oz_tx_frame *isoc_unit = NULL;
905bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly			int nb = pd->nb_queued_isoc_frames;
9066b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil			if (nb >= pd->isoc_latency) {
907bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				struct list_head *e;
908bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				struct oz_tx_frame *f;
909bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				oz_dbg(TX_FRAMES, "Dropping ISOC Unit nb= %d\n",
910bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				       nb);
911bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly				spin_lock(&pd->tx_frame_lock);
9126b029336d93d8f9a94b0256b1f7d9c1768eedba7Surendra Patil				list_for_each(e, &pd->tx_queue) {
913bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly					f = container_of(e, struct oz_tx_frame,
914bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly									link);
9151ec41a31fb695682cab7fc7c1f6ced84d188b6f9Greg Kroah-Hartman					if (f->skb != NULL) {
916bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly						oz_tx_isoc_free(pd, f);
917bc3157dde35ac3b2ec12229edcfaca58cd7925c0Chris Kelly						break;
918					}
919				}
920				spin_unlock(&pd->tx_frame_lock);
921			}
922			isoc_unit = oz_tx_frame_alloc(pd);
923			if (isoc_unit == NULL)
924				goto out;
925			isoc_unit->hdr = oz;
926			isoc_unit->skb = skb;
927			spin_lock_bh(&pd->tx_frame_lock);
928			list_add_tail(&isoc_unit->link, &pd->tx_queue);
929			pd->nb_queued_isoc_frames++;
930			spin_unlock_bh(&pd->tx_frame_lock);
931			oz_dbg(TX_FRAMES,
932			       "Added ISOC Frame to Tx Queue isoc_nb= %d, nb= %d\n",
933			       pd->nb_queued_isoc_frames, pd->nb_queued_frames);
934			return 0;
935		}
936
937		/*In ANYTIME mode Xmit unit immediately*/
938		if (atomic_read(&g_submitted_isoc) < OZ_MAX_SUBMITTED_ISOC) {
939			atomic_inc(&g_submitted_isoc);
940			if (dev_queue_xmit(skb) < 0)
941				return -1;
942			else
943				return 0;
944		}
945
946out:	kfree_skb(skb);
947	return -1;
948
949	}
950	return 0;
951}
952
953/*
954 * Context: process
955 */
956void oz_apps_init(void)
957{
958	int i;
959
960	for (i = 0; i < OZ_APPID_MAX; i++)
961		if (g_app_if[i].init)
962			g_app_if[i].init();
963}
964
965/*
966 * Context: process
967 */
968void oz_apps_term(void)
969{
970	int i;
971
972	/* Terminate all the apps. */
973	for (i = 0; i < OZ_APPID_MAX; i++)
974		if (g_app_if[i].term)
975			g_app_if[i].term();
976}
977
978/*
979 * Context: softirq-serialized
980 */
981void oz_handle_app_elt(struct oz_pd *pd, u8 app_id, struct oz_elt *elt)
982{
983	const struct oz_app_if *ai;
984
985	if (app_id == 0 || app_id > OZ_APPID_MAX)
986		return;
987	ai = &g_app_if[app_id-1];
988	ai->rx(pd, elt);
989}
990
991/*
992 * Context: softirq or process
993 */
994void oz_pd_indicate_farewells(struct oz_pd *pd)
995{
996	struct oz_farewell *f;
997	const struct oz_app_if *ai = &g_app_if[OZ_APPID_USB-1];
998
999	while (1) {
1000		spin_lock_bh(&g_polling_lock);
1001		if (list_empty(&pd->farewell_list)) {
1002			spin_unlock_bh(&g_polling_lock);
1003			break;
1004		}
1005		f = list_first_entry(&pd->farewell_list,
1006				struct oz_farewell, link);
1007		list_del(&f->link);
1008		spin_unlock_bh(&g_polling_lock);
1009		if (ai->farewell)
1010			ai->farewell(pd, f->ep_num, f->report, f->len);
1011		kfree(f);
1012	}
1013}
1014