1/*********************************************************************
2 *
3 * Filename:      irlan_client_event.c
4 * Version:       0.9
5 * Description:   IrLAN client state machine
6 * Status:        Experimental.
7 * Author:        Dag Brattli <dagb@cs.uit.no>
8 * Created at:    Sun Aug 31 20:14:37 1997
9 * Modified at:   Sun Dec 26 21:52:24 1999
10 * Modified by:   Dag Brattli <dagb@cs.uit.no>
11 *
12 *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
13 *     All Rights Reserved.
14 *
15 *     This program is free software; you can redistribute it and/or
16 *     modify it under the terms of the GNU General Public License as
17 *     published by the Free Software Foundation; either version 2 of
18 *     the License, or (at your option) any later version.
19 *
20 *     Neither Dag Brattli nor University of Troms� admit liability nor
21 *     provide warranty for any of this software. This material is
22 *     provided "AS-IS" and at no charge.
23 *
24 ********************************************************************/
25
26#include <linux/skbuff.h>
27
28#include <net/irda/irda.h>
29#include <net/irda/timer.h>
30#include <net/irda/irmod.h>
31#include <net/irda/iriap.h>
32#include <net/irda/irlmp.h>
33#include <net/irda/irttp.h>
34
35#include <net/irda/irlan_common.h>
36#include <net/irda/irlan_client.h>
37#include <net/irda/irlan_event.h>
38
39static int irlan_client_state_idle (struct irlan_cb *self, IRLAN_EVENT event,
40				    struct sk_buff *skb);
41static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
42				    struct sk_buff *skb);
43static int irlan_client_state_conn (struct irlan_cb *self, IRLAN_EVENT event,
44				    struct sk_buff *skb);
45static int irlan_client_state_info (struct irlan_cb *self, IRLAN_EVENT event,
46				    struct sk_buff *skb);
47static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
48				    struct sk_buff *skb);
49static int irlan_client_state_open (struct irlan_cb *self, IRLAN_EVENT event,
50				    struct sk_buff *skb);
51static int irlan_client_state_wait (struct irlan_cb *self, IRLAN_EVENT event,
52				    struct sk_buff *skb);
53static int irlan_client_state_arb  (struct irlan_cb *self, IRLAN_EVENT event,
54				    struct sk_buff *skb);
55static int irlan_client_state_data (struct irlan_cb *self, IRLAN_EVENT event,
56				    struct sk_buff *skb);
57static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
58				    struct sk_buff *skb);
59static int irlan_client_state_sync (struct irlan_cb *self, IRLAN_EVENT event,
60				    struct sk_buff *skb);
61
62static int (*state[])(struct irlan_cb *, IRLAN_EVENT event, struct sk_buff *) =
63{
64	irlan_client_state_idle,
65	irlan_client_state_query,
66	irlan_client_state_conn,
67	irlan_client_state_info,
68	irlan_client_state_media,
69	irlan_client_state_open,
70	irlan_client_state_wait,
71	irlan_client_state_arb,
72	irlan_client_state_data,
73	irlan_client_state_close,
74	irlan_client_state_sync
75};
76
77void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event,
78			   struct sk_buff *skb)
79{
80	IRDA_ASSERT(self != NULL, return;);
81	IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
82
83	(*state[ self->client.state]) (self, event, skb);
84}
85
86/*
87 * Function irlan_client_state_idle (event, skb, info)
88 *
89 *    IDLE, We are waiting for an indication that there is a provider
90 *    available.
91 */
92static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
93				   struct sk_buff *skb)
94{
95	IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
96
97	IRDA_ASSERT(self != NULL, return -1;);
98	IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
99
100	switch (event) {
101	case IRLAN_DISCOVERY_INDICATION:
102		if (self->client.iriap) {
103			IRDA_WARNING("%s(), busy with a previous query\n",
104				     __FUNCTION__);
105			return -EBUSY;
106		}
107
108		self->client.iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
109						irlan_client_get_value_confirm);
110		/* Get some values from peer IAS */
111		irlan_next_client_state(self, IRLAN_QUERY);
112		iriap_getvaluebyclass_request(self->client.iriap,
113					      self->saddr, self->daddr,
114					      "IrLAN", "IrDA:TinyTP:LsapSel");
115		break;
116	case IRLAN_WATCHDOG_TIMEOUT:
117		IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
118		break;
119	default:
120		IRDA_DEBUG(4, "%s(), Unknown event %d\n", __FUNCTION__ , event);
121		break;
122	}
123	if (skb)
124		dev_kfree_skb(skb);
125
126	return 0;
127}
128
129/*
130 * Function irlan_client_state_query (event, skb, info)
131 *
132 *    QUERY, We have queryed the remote IAS and is ready to connect
133 *    to provider, just waiting for the confirm.
134 *
135 */
136static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
137				    struct sk_buff *skb)
138{
139	IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
140
141	IRDA_ASSERT(self != NULL, return -1;);
142	IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
143
144	switch(event) {
145	case IRLAN_IAS_PROVIDER_AVAIL:
146		IRDA_ASSERT(self->dtsap_sel_ctrl != 0, return -1;);
147
148		self->client.open_retries = 0;
149
150		irttp_connect_request(self->client.tsap_ctrl,
151				      self->dtsap_sel_ctrl,
152				      self->saddr, self->daddr, NULL,
153				      IRLAN_MTU, NULL);
154		irlan_next_client_state(self, IRLAN_CONN);
155		break;
156	case IRLAN_IAS_PROVIDER_NOT_AVAIL:
157		IRDA_DEBUG(2, "%s(), IAS_PROVIDER_NOT_AVAIL\n", __FUNCTION__ );
158		irlan_next_client_state(self, IRLAN_IDLE);
159
160		/* Give the client a kick! */
161		if ((self->provider.access_type == ACCESS_PEER) &&
162		    (self->provider.state != IRLAN_IDLE))
163			irlan_client_wakeup(self, self->saddr, self->daddr);
164		break;
165	case IRLAN_LMP_DISCONNECT:
166	case IRLAN_LAP_DISCONNECT:
167		irlan_next_client_state(self, IRLAN_IDLE);
168		break;
169	case IRLAN_WATCHDOG_TIMEOUT:
170		IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
171		break;
172	default:
173		IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
174		break;
175	}
176	if (skb)
177		dev_kfree_skb(skb);
178
179	return 0;
180}
181
182/*
183 * Function irlan_client_state_conn (event, skb, info)
184 *
185 *    CONN, We have connected to a provider but has not issued any
186 *    commands yet.
187 *
188 */
189static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event,
190				   struct sk_buff *skb)
191{
192	IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
193
194	IRDA_ASSERT(self != NULL, return -1;);
195
196	switch (event) {
197	case IRLAN_CONNECT_COMPLETE:
198		/* Send getinfo cmd */
199		irlan_get_provider_info(self);
200		irlan_next_client_state(self, IRLAN_INFO);
201		break;
202	case IRLAN_LMP_DISCONNECT:
203	case IRLAN_LAP_DISCONNECT:
204		irlan_next_client_state(self, IRLAN_IDLE);
205		break;
206	case IRLAN_WATCHDOG_TIMEOUT:
207		IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
208		break;
209	default:
210		IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
211		break;
212	}
213	if (skb)
214		dev_kfree_skb(skb);
215
216	return 0;
217}
218
219/*
220 * Function irlan_client_state_info (self, event, skb, info)
221 *
222 *    INFO, We have issued a GetInfo command and is awaiting a reply.
223 */
224static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event,
225				   struct sk_buff *skb)
226{
227	IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
228
229	IRDA_ASSERT(self != NULL, return -1;);
230
231	switch (event) {
232	case IRLAN_DATA_INDICATION:
233		IRDA_ASSERT(skb != NULL, return -1;);
234
235		irlan_client_parse_response(self, skb);
236
237		irlan_next_client_state(self, IRLAN_MEDIA);
238
239		irlan_get_media_char(self);
240		break;
241
242	case IRLAN_LMP_DISCONNECT:
243	case IRLAN_LAP_DISCONNECT:
244		irlan_next_client_state(self, IRLAN_IDLE);
245		break;
246	case IRLAN_WATCHDOG_TIMEOUT:
247		IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
248		break;
249	default:
250		IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
251		break;
252	}
253	if (skb)
254		dev_kfree_skb(skb);
255
256	return 0;
257}
258
259/*
260 * Function irlan_client_state_media (self, event, skb, info)
261 *
262 *    MEDIA, The irlan_client has issued a GetMedia command and is awaiting a
263 *    reply.
264 *
265 */
266static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
267				    struct sk_buff *skb)
268{
269	IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
270
271	IRDA_ASSERT(self != NULL, return -1;);
272
273	switch(event) {
274	case IRLAN_DATA_INDICATION:
275		irlan_client_parse_response(self, skb);
276		irlan_open_data_channel(self);
277		irlan_next_client_state(self, IRLAN_OPEN);
278		break;
279	case IRLAN_LMP_DISCONNECT:
280	case IRLAN_LAP_DISCONNECT:
281		irlan_next_client_state(self, IRLAN_IDLE);
282		break;
283	case IRLAN_WATCHDOG_TIMEOUT:
284		IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
285		break;
286	default:
287		IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
288		break;
289	}
290	if (skb)
291		dev_kfree_skb(skb);
292
293	return 0;
294}
295
296/*
297 * Function irlan_client_state_open (self, event, skb, info)
298 *
299 *    OPEN, The irlan_client has issued a OpenData command and is awaiting a
300 *    reply
301 *
302 */
303static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event,
304				   struct sk_buff *skb)
305{
306	struct qos_info qos;
307
308	IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
309
310	IRDA_ASSERT(self != NULL, return -1;);
311
312	switch(event) {
313	case IRLAN_DATA_INDICATION:
314		irlan_client_parse_response(self, skb);
315
316		/*
317		 *  Check if we have got the remote TSAP for data
318		 *  communications
319		 */
320		IRDA_ASSERT(self->dtsap_sel_data != 0, return -1;);
321
322		/* Check which access type we are dealing with */
323		switch (self->client.access_type) {
324		case ACCESS_PEER:
325		    if (self->provider.state == IRLAN_OPEN) {
326
327			    irlan_next_client_state(self, IRLAN_ARB);
328			    irlan_do_client_event(self, IRLAN_CHECK_CON_ARB,
329						  NULL);
330		    } else {
331
332			    irlan_next_client_state(self, IRLAN_WAIT);
333		    }
334		    break;
335		case ACCESS_DIRECT:
336		case ACCESS_HOSTED:
337			qos.link_disc_time.bits = 0x01; /* 3 secs */
338
339			irttp_connect_request(self->tsap_data,
340					      self->dtsap_sel_data,
341					      self->saddr, self->daddr, &qos,
342					      IRLAN_MTU, NULL);
343
344			irlan_next_client_state(self, IRLAN_DATA);
345			break;
346		default:
347			IRDA_DEBUG(2, "%s(), unknown access type!\n", __FUNCTION__ );
348			break;
349		}
350		break;
351	case IRLAN_LMP_DISCONNECT:
352	case IRLAN_LAP_DISCONNECT:
353		irlan_next_client_state(self, IRLAN_IDLE);
354		break;
355	case IRLAN_WATCHDOG_TIMEOUT:
356		IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
357		break;
358	default:
359		IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
360		break;
361	}
362
363	if (skb)
364		dev_kfree_skb(skb);
365
366	return 0;
367}
368
369/*
370 * Function irlan_client_state_wait (self, event, skb, info)
371 *
372 *    WAIT, The irlan_client is waiting for the local provider to enter the
373 *    provider OPEN state.
374 *
375 */
376static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event,
377				   struct sk_buff *skb)
378{
379	IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
380
381	IRDA_ASSERT(self != NULL, return -1;);
382
383	switch(event) {
384	case IRLAN_PROVIDER_SIGNAL:
385		irlan_next_client_state(self, IRLAN_ARB);
386		irlan_do_client_event(self, IRLAN_CHECK_CON_ARB, NULL);
387		break;
388	case IRLAN_LMP_DISCONNECT:
389	case IRLAN_LAP_DISCONNECT:
390		irlan_next_client_state(self, IRLAN_IDLE);
391		break;
392	case IRLAN_WATCHDOG_TIMEOUT:
393		IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
394		break;
395	default:
396		IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
397		break;
398	}
399	if (skb)
400		dev_kfree_skb(skb);
401
402	return 0;
403}
404
405static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event,
406				  struct sk_buff *skb)
407{
408	struct qos_info qos;
409
410	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
411
412	IRDA_ASSERT(self != NULL, return -1;);
413
414	switch(event) {
415	case IRLAN_CHECK_CON_ARB:
416		if (self->client.recv_arb_val == self->provider.send_arb_val) {
417			irlan_next_client_state(self, IRLAN_CLOSE);
418			irlan_close_data_channel(self);
419		} else if (self->client.recv_arb_val <
420			   self->provider.send_arb_val)
421		{
422			qos.link_disc_time.bits = 0x01; /* 3 secs */
423
424			irlan_next_client_state(self, IRLAN_DATA);
425			irttp_connect_request(self->tsap_data,
426					      self->dtsap_sel_data,
427					      self->saddr, self->daddr, &qos,
428					      IRLAN_MTU, NULL);
429		} else if (self->client.recv_arb_val >
430			   self->provider.send_arb_val)
431		{
432			IRDA_DEBUG(2, "%s(), lost the battle :-(\n", __FUNCTION__ );
433		}
434		break;
435	case IRLAN_DATA_CONNECT_INDICATION:
436		irlan_next_client_state(self, IRLAN_DATA);
437		break;
438	case IRLAN_LMP_DISCONNECT:
439	case IRLAN_LAP_DISCONNECT:
440		irlan_next_client_state(self, IRLAN_IDLE);
441		break;
442	case IRLAN_WATCHDOG_TIMEOUT:
443		IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
444		break;
445	default:
446		IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
447		break;
448	}
449	if (skb)
450		dev_kfree_skb(skb);
451
452	return 0;
453}
454
455/*
456 * Function irlan_client_state_data (self, event, skb, info)
457 *
458 *    DATA, The data channel is connected, allowing data transfers between
459 *    the local and remote machines.
460 *
461 */
462static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event,
463				   struct sk_buff *skb)
464{
465	IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
466
467	IRDA_ASSERT(self != NULL, return -1;);
468	IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
469
470	switch(event) {
471	case IRLAN_DATA_INDICATION:
472		irlan_client_parse_response(self, skb);
473		break;
474	case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
475	case IRLAN_LAP_DISCONNECT:
476		irlan_next_client_state(self, IRLAN_IDLE);
477		break;
478	default:
479		IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
480		break;
481	}
482	if (skb)
483		dev_kfree_skb(skb);
484
485	return 0;
486}
487
488/*
489 * Function irlan_client_state_close (self, event, skb, info)
490 *
491 *
492 *
493 */
494static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
495				    struct sk_buff *skb)
496{
497	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
498
499	if (skb)
500		dev_kfree_skb(skb);
501
502	return 0;
503}
504
505/*
506 * Function irlan_client_state_sync (self, event, skb, info)
507 *
508 *
509 *
510 */
511static int irlan_client_state_sync(struct irlan_cb *self, IRLAN_EVENT event,
512				   struct sk_buff *skb)
513{
514	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
515
516	if (skb)
517		dev_kfree_skb(skb);
518
519	return 0;
520}
521