summaryrefslogtreecommitdiffstats
path: root/net/netbeui/netbeui_llc.c
blob: 29edc5acf7598981be809b252bc2f4f944140b9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
 *	NET3:	802.2 LLC supervisor for the netbeui protocols. 
 *
 *	The basic aim is to provide a self managing link layer supervisor
 *	for netbeui. It creates and destroys the 802.2 virtual connections
 *	as needed, and copes with the various races when a link goes down
 *	just as its requested etc.
 *
 *	The upper layers are presented with the notion of an nb_link which
 *	is a potentially shared object that represents a logical path 
 *	between two hosts. Each nb_link has usage counts and users can
 *	treat it as if its their own.
 */
 
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/notifier.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/datalink.h>
#include <net/p8022.h>
#include <net/psnap.h>
#include <net/sock.h>
#include <net/llc.h>
#include <net/netbeui.h>


/*
 *	When this routine is called the netbeui layer has decided to
 *	drop the link. There is a tiny risk that we might reuse the
 *	link after we decide. Thus before we blast the link into little
 *	tiny pieces we must check....
 */
 
static void netbeui_do_destroy(struct nb_link *nb)
{
	/*
	 *	Are we wanted again. Bring it back. Sigh, wish people
	 *	would make up their minds 8)
	 */
	if(nb->users>0)
	{
		nb->state=NETBEUI_CONNWAIT;
		llc_connect_request(&nb->llc);
		return;
	}
	/*
	 *	Blam.... into oblivion it goes
	 */
	
	llc_unregister(&nb->llc);
	netbeui_free_link(nb);
}

/*
 *	Handle netbeui events. Basically that means keep it up when it
 *	should be up, down when it should be down and handle all the data.
 */

static void netbeui_event(llcptr llc)
{
	struct nb_link *nb=(struct nb_link *)llc;
	
	/*
	 *	See what has occured
	 */
	 

	/*
	 *	Connect completion confirmation
	 */
	 
	if(llc->llc_callbacks&LLC_CONN_CONFIRM)
	{
		/*
		 *	Link up if desired. Otherwise try frantically
		 *	to close it.
		 */
		if(nb->state!=NETBEUI_DEADWAIT)
		{
			/*
			 *	Wake pending writers
			 */
			nb->state=NETBEUI_OPEN;
			netbeui_wakeup(nb);
		}
		else
			llc_disconnect_request(llc);
	}
	
	/*
	 *	Data is passed to the upper netbeui layer
	 */

	if(llc->llc_callbacks&LLC_DATA_INDIC)
	{
		netbeu_rcv_stream(llc,llc->inc_skb);
		/*
		 *	Frame free is controlled by our stream processor
		 */
		return;
	}

	/*
	 *	We got disconnected
	 */
	 
	if(llc->llc_callbacks&LLC_DISC_INDICATION)
	{
		if(nb->state==NETBEUI_DEADWAIT)
		{
			netbeui_do_destroy(nb);
			return;
		}
		if(nb->state==NETBEUI_DISCWAIT)
		{
			llc_connect_request(llc);
			nb->state=NETBEUI_CONNWAIT;
		}
	}
	
	/*
	 *	Miscellaneous burps
	 */
	
	if(llc->llc_callbacks&(LLC_RESET_INDIC_LOC|LLC_RESET_INDIC_REM|
					LLC_RST_CONFIRM))
	{
		/*
		 *	Reset. 
		 *	Q: Is tearing the link down the right answer ?
		 *
		 *	For now we just carry on
		 */
	}

	/*
	 *	Track link busy status
	 */
	 
	if(llc->llc_callbacks&LLC_REMOTE_BUSY)
		nb->busy=1;	/* Send no more for a bit */
	if(llc->llc_callbacks&LLC_REMOTE_NOTBUSY)
	{
		/* Coming unbusy may wake sending threads */
		nb->busy=0;
		netbeui_wakeup(nb);
	}		
	/*
	 *	UI frames are passed to the upper netbeui layer.
	 */
	if(llc->llc_callbacks&LLC_UI_DATA)
	{
		netbeui_rcv_dgram(llc,llc->inc_skb);
		return;
	}

	/* We ignore TST, XID, FRMR stuff */
	/* FIXME: We need to free frames here once I fix the callback! */
	if(llc->inc_skb)
		kfree_skb(skb);
}

/*
 *	Netbeui has created a new logical link. As a result we will
 *	need to find or create a suitable 802.2 LLC session and join
 *	it.
 */

struct nb_link *netbeui_create_channel(struct device *dev, u8 *remote_mac, int pri)
{
	struct nb_link *nb=netbeui_find_channel(dev,remote_mac);
	if(nb)
	{
		if(nb->state==NETBEUI_DEADWAIT)
		{
			/*
			 *	We had commenced a final shutdown. We
			 *	cannot abort that (we sent the packet) but
			 *	we can shift the mode to DISCWAIT. That will
			 *	cause the disconnect event to bounce us
			 *	back into connected state.
			 */
			nb->state==NETBEUI_DISCWAIT;
		}
		nb->users++;
		return nb;
	}
	nb=netbeui_alloc_link(pri);
	if(nb==NULL)
		return NULL;
	
	/*
	 *	Internal book keeping
	 */
	 
	nb->dev=dev;
	nb->users=1;
	nb->busy=0;
	nb->wakeup=NULL;
	nb->state=NETBEUI_CONNWAIT;
	memcpy(nb->remote_mac, remote_mac, ETH_ALEN);
	
	/*
	 *	Now try and attach an LLC.
	 */
	
	if(register_cl2llc_client(&nb->llc,dev->name,netbeui_event,
		remote_mac, NETBEUI_SAP, NETBEUI_SAP)<0)
	{
		netbeui_free_link(nb);
		return NULL;
	}
	
	/*
	 *	Commence connection establishment.
	 */
	 
	llc_connect_request(&nb->llc);
	
	/*
	 *	Done
	 */

	nb->next=nb_link_list;
	nb_link_list=nb;
	 
	return nb;
}

/*
 *	A logical netbeui channel has died. If the channel has no
 *	further users we commence shutdown.
 */
	
int netbeui_delete_channel(struct nb_link *nb)
{
	nb->users--;
	
	/*
	 *	FIXME: Must remove ourselves from the nb_link chain when
	 *	we add that bit
	 */
	 
	if(nb->users)
		return 0;
		
	/*
	 *	Ensure we drop soon. The disconnect confirm will let
	 *	us fix the deletion. If someone wants the link at
	 *	the wrong moment nothing bad will occur. The create
	 *	or the do_destroy will sort it.
	 */

	nb->state = NETBEUI_DEADWAIT;
	llc_disconnect_request(lp);
	return 0;
}