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_ASSERT(self != NULL, return -1;);
96	IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
97
98	switch (event) {
99	case IRLAN_DISCOVERY_INDICATION:
100		if (self->client.iriap) {
101			net_warn_ratelimited("%s(), busy with a previous query\n",
102					     __func__);
103			return -EBUSY;
104		}
105
106		self->client.iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
107						irlan_client_get_value_confirm);
108		/* Get some values from peer IAS */
109		irlan_next_client_state(self, IRLAN_QUERY);
110		iriap_getvaluebyclass_request(self->client.iriap,
111					      self->saddr, self->daddr,
112					      "IrLAN", "IrDA:TinyTP:LsapSel");
113		break;
114	case IRLAN_WATCHDOG_TIMEOUT:
115		pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
116		break;
117	default:
118		pr_debug("%s(), Unknown event %d\n", __func__ , event);
119		break;
120	}
121	if (skb)
122		dev_kfree_skb(skb);
123
124	return 0;
125}
126
127/*
128 * Function irlan_client_state_query (event, skb, info)
129 *
130 *    QUERY, We have queryed the remote IAS and is ready to connect
131 *    to provider, just waiting for the confirm.
132 *
133 */
134static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
135				    struct sk_buff *skb)
136{
137	IRDA_ASSERT(self != NULL, return -1;);
138	IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
139
140	switch(event) {
141	case IRLAN_IAS_PROVIDER_AVAIL:
142		IRDA_ASSERT(self->dtsap_sel_ctrl != 0, return -1;);
143
144		self->client.open_retries = 0;
145
146		irttp_connect_request(self->client.tsap_ctrl,
147				      self->dtsap_sel_ctrl,
148				      self->saddr, self->daddr, NULL,
149				      IRLAN_MTU, NULL);
150		irlan_next_client_state(self, IRLAN_CONN);
151		break;
152	case IRLAN_IAS_PROVIDER_NOT_AVAIL:
153		pr_debug("%s(), IAS_PROVIDER_NOT_AVAIL\n", __func__);
154		irlan_next_client_state(self, IRLAN_IDLE);
155
156		/* Give the client a kick! */
157		if ((self->provider.access_type == ACCESS_PEER) &&
158		    (self->provider.state != IRLAN_IDLE))
159			irlan_client_wakeup(self, self->saddr, self->daddr);
160		break;
161	case IRLAN_LMP_DISCONNECT:
162	case IRLAN_LAP_DISCONNECT:
163		irlan_next_client_state(self, IRLAN_IDLE);
164		break;
165	case IRLAN_WATCHDOG_TIMEOUT:
166		pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
167		break;
168	default:
169		pr_debug("%s(), Unknown event %d\n", __func__ , event);
170		break;
171	}
172	if (skb)
173		dev_kfree_skb(skb);
174
175	return 0;
176}
177
178/*
179 * Function irlan_client_state_conn (event, skb, info)
180 *
181 *    CONN, We have connected to a provider but has not issued any
182 *    commands yet.
183 *
184 */
185static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event,
186				   struct sk_buff *skb)
187{
188	IRDA_ASSERT(self != NULL, return -1;);
189
190	switch (event) {
191	case IRLAN_CONNECT_COMPLETE:
192		/* Send getinfo cmd */
193		irlan_get_provider_info(self);
194		irlan_next_client_state(self, IRLAN_INFO);
195		break;
196	case IRLAN_LMP_DISCONNECT:
197	case IRLAN_LAP_DISCONNECT:
198		irlan_next_client_state(self, IRLAN_IDLE);
199		break;
200	case IRLAN_WATCHDOG_TIMEOUT:
201		pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
202		break;
203	default:
204		pr_debug("%s(), Unknown event %d\n", __func__ , event);
205		break;
206	}
207	if (skb)
208		dev_kfree_skb(skb);
209
210	return 0;
211}
212
213/*
214 * Function irlan_client_state_info (self, event, skb, info)
215 *
216 *    INFO, We have issued a GetInfo command and is awaiting a reply.
217 */
218static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event,
219				   struct sk_buff *skb)
220{
221	IRDA_ASSERT(self != NULL, return -1;);
222
223	switch (event) {
224	case IRLAN_DATA_INDICATION:
225		IRDA_ASSERT(skb != NULL, return -1;);
226
227		irlan_client_parse_response(self, skb);
228
229		irlan_next_client_state(self, IRLAN_MEDIA);
230
231		irlan_get_media_char(self);
232		break;
233
234	case IRLAN_LMP_DISCONNECT:
235	case IRLAN_LAP_DISCONNECT:
236		irlan_next_client_state(self, IRLAN_IDLE);
237		break;
238	case IRLAN_WATCHDOG_TIMEOUT:
239		pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
240		break;
241	default:
242		pr_debug("%s(), Unknown event %d\n", __func__ , event);
243		break;
244	}
245	if (skb)
246		dev_kfree_skb(skb);
247
248	return 0;
249}
250
251/*
252 * Function irlan_client_state_media (self, event, skb, info)
253 *
254 *    MEDIA, The irlan_client has issued a GetMedia command and is awaiting a
255 *    reply.
256 *
257 */
258static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
259				    struct sk_buff *skb)
260{
261	IRDA_ASSERT(self != NULL, return -1;);
262
263	switch(event) {
264	case IRLAN_DATA_INDICATION:
265		irlan_client_parse_response(self, skb);
266		irlan_open_data_channel(self);
267		irlan_next_client_state(self, IRLAN_OPEN);
268		break;
269	case IRLAN_LMP_DISCONNECT:
270	case IRLAN_LAP_DISCONNECT:
271		irlan_next_client_state(self, IRLAN_IDLE);
272		break;
273	case IRLAN_WATCHDOG_TIMEOUT:
274		pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
275		break;
276	default:
277		pr_debug("%s(), Unknown event %d\n", __func__ , event);
278		break;
279	}
280	if (skb)
281		dev_kfree_skb(skb);
282
283	return 0;
284}
285
286/*
287 * Function irlan_client_state_open (self, event, skb, info)
288 *
289 *    OPEN, The irlan_client has issued a OpenData command and is awaiting a
290 *    reply
291 *
292 */
293static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event,
294				   struct sk_buff *skb)
295{
296	struct qos_info qos;
297
298	IRDA_ASSERT(self != NULL, return -1;);
299
300	switch(event) {
301	case IRLAN_DATA_INDICATION:
302		irlan_client_parse_response(self, skb);
303
304		/*
305		 *  Check if we have got the remote TSAP for data
306		 *  communications
307		 */
308		IRDA_ASSERT(self->dtsap_sel_data != 0, return -1;);
309
310		/* Check which access type we are dealing with */
311		switch (self->client.access_type) {
312		case ACCESS_PEER:
313		    if (self->provider.state == IRLAN_OPEN) {
314
315			    irlan_next_client_state(self, IRLAN_ARB);
316			    irlan_do_client_event(self, IRLAN_CHECK_CON_ARB,
317						  NULL);
318		    } else {
319
320			    irlan_next_client_state(self, IRLAN_WAIT);
321		    }
322		    break;
323		case ACCESS_DIRECT:
324		case ACCESS_HOSTED:
325			qos.link_disc_time.bits = 0x01; /* 3 secs */
326
327			irttp_connect_request(self->tsap_data,
328					      self->dtsap_sel_data,
329					      self->saddr, self->daddr, &qos,
330					      IRLAN_MTU, NULL);
331
332			irlan_next_client_state(self, IRLAN_DATA);
333			break;
334		default:
335			pr_debug("%s(), unknown access type!\n", __func__);
336			break;
337		}
338		break;
339	case IRLAN_LMP_DISCONNECT:
340	case IRLAN_LAP_DISCONNECT:
341		irlan_next_client_state(self, IRLAN_IDLE);
342		break;
343	case IRLAN_WATCHDOG_TIMEOUT:
344		pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
345		break;
346	default:
347		pr_debug("%s(), Unknown event %d\n", __func__ , event);
348		break;
349	}
350
351	if (skb)
352		dev_kfree_skb(skb);
353
354	return 0;
355}
356
357/*
358 * Function irlan_client_state_wait (self, event, skb, info)
359 *
360 *    WAIT, The irlan_client is waiting for the local provider to enter the
361 *    provider OPEN state.
362 *
363 */
364static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event,
365				   struct sk_buff *skb)
366{
367	IRDA_ASSERT(self != NULL, return -1;);
368
369	switch(event) {
370	case IRLAN_PROVIDER_SIGNAL:
371		irlan_next_client_state(self, IRLAN_ARB);
372		irlan_do_client_event(self, IRLAN_CHECK_CON_ARB, NULL);
373		break;
374	case IRLAN_LMP_DISCONNECT:
375	case IRLAN_LAP_DISCONNECT:
376		irlan_next_client_state(self, IRLAN_IDLE);
377		break;
378	case IRLAN_WATCHDOG_TIMEOUT:
379		pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
380		break;
381	default:
382		pr_debug("%s(), Unknown event %d\n", __func__ , event);
383		break;
384	}
385	if (skb)
386		dev_kfree_skb(skb);
387
388	return 0;
389}
390
391static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event,
392				  struct sk_buff *skb)
393{
394	struct qos_info qos;
395
396	IRDA_ASSERT(self != NULL, return -1;);
397
398	switch(event) {
399	case IRLAN_CHECK_CON_ARB:
400		if (self->client.recv_arb_val == self->provider.send_arb_val) {
401			irlan_next_client_state(self, IRLAN_CLOSE);
402			irlan_close_data_channel(self);
403		} else if (self->client.recv_arb_val <
404			   self->provider.send_arb_val)
405		{
406			qos.link_disc_time.bits = 0x01; /* 3 secs */
407
408			irlan_next_client_state(self, IRLAN_DATA);
409			irttp_connect_request(self->tsap_data,
410					      self->dtsap_sel_data,
411					      self->saddr, self->daddr, &qos,
412					      IRLAN_MTU, NULL);
413		} else if (self->client.recv_arb_val >
414			   self->provider.send_arb_val)
415		{
416			pr_debug("%s(), lost the battle :-(\n", __func__);
417		}
418		break;
419	case IRLAN_DATA_CONNECT_INDICATION:
420		irlan_next_client_state(self, IRLAN_DATA);
421		break;
422	case IRLAN_LMP_DISCONNECT:
423	case IRLAN_LAP_DISCONNECT:
424		irlan_next_client_state(self, IRLAN_IDLE);
425		break;
426	case IRLAN_WATCHDOG_TIMEOUT:
427		pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
428		break;
429	default:
430		pr_debug("%s(), Unknown event %d\n", __func__ , event);
431		break;
432	}
433	if (skb)
434		dev_kfree_skb(skb);
435
436	return 0;
437}
438
439/*
440 * Function irlan_client_state_data (self, event, skb, info)
441 *
442 *    DATA, The data channel is connected, allowing data transfers between
443 *    the local and remote machines.
444 *
445 */
446static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event,
447				   struct sk_buff *skb)
448{
449	IRDA_ASSERT(self != NULL, return -1;);
450	IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
451
452	switch(event) {
453	case IRLAN_DATA_INDICATION:
454		irlan_client_parse_response(self, skb);
455		break;
456	case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
457	case IRLAN_LAP_DISCONNECT:
458		irlan_next_client_state(self, IRLAN_IDLE);
459		break;
460	default:
461		pr_debug("%s(), Unknown event %d\n", __func__ , event);
462		break;
463	}
464	if (skb)
465		dev_kfree_skb(skb);
466
467	return 0;
468}
469
470/*
471 * Function irlan_client_state_close (self, event, skb, info)
472 *
473 *
474 *
475 */
476static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
477				    struct sk_buff *skb)
478{
479	if (skb)
480		dev_kfree_skb(skb);
481
482	return 0;
483}
484
485/*
486 * Function irlan_client_state_sync (self, event, skb, info)
487 *
488 *
489 *
490 */
491static int irlan_client_state_sync(struct irlan_cb *self, IRLAN_EVENT event,
492				   struct sk_buff *skb)
493{
494	if (skb)
495		dev_kfree_skb(skb);
496
497	return 0;
498}
499
500
501
502
503
504
505
506
507
508
509
510
511
512