diff options
Diffstat (limited to 'net/irda/irlmp_event.c')
-rw-r--r-- | net/irda/irlmp_event.c | 912 |
1 files changed, 912 insertions, 0 deletions
diff --git a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c new file mode 100644 index 000000000000..26649f6528e6 --- /dev/null +++ b/net/irda/irlmp_event.c | |||
@@ -0,0 +1,912 @@ | |||
1 | /********************************************************************* | ||
2 | * | ||
3 | * Filename: irlmp_event.c | ||
4 | * Version: 0.8 | ||
5 | * Description: An IrDA LMP event driver for Linux | ||
6 | * Status: Experimental. | ||
7 | * Author: Dag Brattli <dagb@cs.uit.no> | ||
8 | * Created at: Mon Aug 4 20:40:53 1997 | ||
9 | * Modified at: Tue Dec 14 23:04:16 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 | * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com> | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or | ||
17 | * modify it under the terms of the GNU General Public License as | ||
18 | * published by the Free Software Foundation; either version 2 of | ||
19 | * the License, or (at your option) any later version. | ||
20 | * | ||
21 | * Neither Dag Brattli nor University of Tromsų admit liability nor | ||
22 | * provide warranty for any of this software. This material is | ||
23 | * provided "AS-IS" and at no charge. | ||
24 | * | ||
25 | ********************************************************************/ | ||
26 | |||
27 | #include <linux/config.h> | ||
28 | #include <linux/kernel.h> | ||
29 | |||
30 | #include <net/irda/irda.h> | ||
31 | #include <net/irda/timer.h> | ||
32 | #include <net/irda/irlap.h> | ||
33 | #include <net/irda/irlmp.h> | ||
34 | #include <net/irda/irlmp_frame.h> | ||
35 | #include <net/irda/irlmp_event.h> | ||
36 | |||
37 | const char *irlmp_state[] = { | ||
38 | "LAP_STANDBY", | ||
39 | "LAP_U_CONNECT", | ||
40 | "LAP_ACTIVE", | ||
41 | }; | ||
42 | |||
43 | const char *irlsap_state[] = { | ||
44 | "LSAP_DISCONNECTED", | ||
45 | "LSAP_CONNECT", | ||
46 | "LSAP_CONNECT_PEND", | ||
47 | "LSAP_DATA_TRANSFER_READY", | ||
48 | "LSAP_SETUP", | ||
49 | "LSAP_SETUP_PEND", | ||
50 | }; | ||
51 | |||
52 | #ifdef CONFIG_IRDA_DEBUG | ||
53 | static const char *irlmp_event[] = { | ||
54 | "LM_CONNECT_REQUEST", | ||
55 | "LM_CONNECT_CONFIRM", | ||
56 | "LM_CONNECT_RESPONSE", | ||
57 | "LM_CONNECT_INDICATION", | ||
58 | |||
59 | "LM_DISCONNECT_INDICATION", | ||
60 | "LM_DISCONNECT_REQUEST", | ||
61 | |||
62 | "LM_DATA_REQUEST", | ||
63 | "LM_UDATA_REQUEST", | ||
64 | "LM_DATA_INDICATION", | ||
65 | "LM_UDATA_INDICATION", | ||
66 | |||
67 | "LM_WATCHDOG_TIMEOUT", | ||
68 | |||
69 | /* IrLAP events */ | ||
70 | "LM_LAP_CONNECT_REQUEST", | ||
71 | "LM_LAP_CONNECT_INDICATION", | ||
72 | "LM_LAP_CONNECT_CONFIRM", | ||
73 | "LM_LAP_DISCONNECT_INDICATION", | ||
74 | "LM_LAP_DISCONNECT_REQUEST", | ||
75 | "LM_LAP_DISCOVERY_REQUEST", | ||
76 | "LM_LAP_DISCOVERY_CONFIRM", | ||
77 | "LM_LAP_IDLE_TIMEOUT", | ||
78 | }; | ||
79 | #endif /* CONFIG_IRDA_DEBUG */ | ||
80 | |||
81 | /* LAP Connection control proto declarations */ | ||
82 | static void irlmp_state_standby (struct lap_cb *, IRLMP_EVENT, | ||
83 | struct sk_buff *); | ||
84 | static void irlmp_state_u_connect(struct lap_cb *, IRLMP_EVENT, | ||
85 | struct sk_buff *); | ||
86 | static void irlmp_state_active (struct lap_cb *, IRLMP_EVENT, | ||
87 | struct sk_buff *); | ||
88 | |||
89 | /* LSAP Connection control proto declarations */ | ||
90 | static int irlmp_state_disconnected(struct lsap_cb *, IRLMP_EVENT, | ||
91 | struct sk_buff *); | ||
92 | static int irlmp_state_connect (struct lsap_cb *, IRLMP_EVENT, | ||
93 | struct sk_buff *); | ||
94 | static int irlmp_state_connect_pend(struct lsap_cb *, IRLMP_EVENT, | ||
95 | struct sk_buff *); | ||
96 | static int irlmp_state_dtr (struct lsap_cb *, IRLMP_EVENT, | ||
97 | struct sk_buff *); | ||
98 | static int irlmp_state_setup (struct lsap_cb *, IRLMP_EVENT, | ||
99 | struct sk_buff *); | ||
100 | static int irlmp_state_setup_pend (struct lsap_cb *, IRLMP_EVENT, | ||
101 | struct sk_buff *); | ||
102 | |||
103 | static void (*lap_state[]) (struct lap_cb *, IRLMP_EVENT, struct sk_buff *) = | ||
104 | { | ||
105 | irlmp_state_standby, | ||
106 | irlmp_state_u_connect, | ||
107 | irlmp_state_active, | ||
108 | }; | ||
109 | |||
110 | static int (*lsap_state[])( struct lsap_cb *, IRLMP_EVENT, struct sk_buff *) = | ||
111 | { | ||
112 | irlmp_state_disconnected, | ||
113 | irlmp_state_connect, | ||
114 | irlmp_state_connect_pend, | ||
115 | irlmp_state_dtr, | ||
116 | irlmp_state_setup, | ||
117 | irlmp_state_setup_pend | ||
118 | }; | ||
119 | |||
120 | static inline void irlmp_next_lap_state(struct lap_cb *self, | ||
121 | IRLMP_STATE state) | ||
122 | { | ||
123 | /* | ||
124 | IRDA_DEBUG(4, "%s(), LMP LAP = %s\n", __FUNCTION__, irlmp_state[state]); | ||
125 | */ | ||
126 | self->lap_state = state; | ||
127 | } | ||
128 | |||
129 | static inline void irlmp_next_lsap_state(struct lsap_cb *self, | ||
130 | LSAP_STATE state) | ||
131 | { | ||
132 | /* | ||
133 | IRDA_ASSERT(self != NULL, return;); | ||
134 | IRDA_DEBUG(4, "%s(), LMP LSAP = %s\n", __FUNCTION__, irlsap_state[state]); | ||
135 | */ | ||
136 | self->lsap_state = state; | ||
137 | } | ||
138 | |||
139 | /* Do connection control events */ | ||
140 | int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event, | ||
141 | struct sk_buff *skb) | ||
142 | { | ||
143 | IRDA_ASSERT(self != NULL, return -1;); | ||
144 | IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); | ||
145 | |||
146 | IRDA_DEBUG(4, "%s(), EVENT = %s, STATE = %s\n", | ||
147 | __FUNCTION__, irlmp_event[event], irlsap_state[ self->lsap_state]); | ||
148 | |||
149 | return (*lsap_state[self->lsap_state]) (self, event, skb); | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * Function do_lap_event (event, skb, info) | ||
154 | * | ||
155 | * Do IrLAP control events | ||
156 | * | ||
157 | */ | ||
158 | void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event, | ||
159 | struct sk_buff *skb) | ||
160 | { | ||
161 | IRDA_ASSERT(self != NULL, return;); | ||
162 | IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); | ||
163 | |||
164 | IRDA_DEBUG(4, "%s(), EVENT = %s, STATE = %s\n", __FUNCTION__, | ||
165 | irlmp_event[event], | ||
166 | irlmp_state[self->lap_state]); | ||
167 | |||
168 | (*lap_state[self->lap_state]) (self, event, skb); | ||
169 | } | ||
170 | |||
171 | void irlmp_discovery_timer_expired(void *data) | ||
172 | { | ||
173 | IRDA_DEBUG(4, "%s()\n", __FUNCTION__); | ||
174 | |||
175 | /* We always cleanup the log (active & passive discovery) */ | ||
176 | irlmp_do_expiry(); | ||
177 | |||
178 | /* Active discovery is conditional */ | ||
179 | if (sysctl_discovery) | ||
180 | irlmp_do_discovery(sysctl_discovery_slots); | ||
181 | |||
182 | /* Restart timer */ | ||
183 | irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout * HZ); | ||
184 | } | ||
185 | |||
186 | void irlmp_watchdog_timer_expired(void *data) | ||
187 | { | ||
188 | struct lsap_cb *self = (struct lsap_cb *) data; | ||
189 | |||
190 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | ||
191 | |||
192 | IRDA_ASSERT(self != NULL, return;); | ||
193 | IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); | ||
194 | |||
195 | irlmp_do_lsap_event(self, LM_WATCHDOG_TIMEOUT, NULL); | ||
196 | } | ||
197 | |||
198 | void irlmp_idle_timer_expired(void *data) | ||
199 | { | ||
200 | struct lap_cb *self = (struct lap_cb *) data; | ||
201 | |||
202 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | ||
203 | |||
204 | IRDA_ASSERT(self != NULL, return;); | ||
205 | IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); | ||
206 | |||
207 | irlmp_do_lap_event(self, LM_LAP_IDLE_TIMEOUT, NULL); | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Send an event on all LSAPs attached to this LAP. | ||
212 | */ | ||
213 | static inline void | ||
214 | irlmp_do_all_lsap_event(hashbin_t * lsap_hashbin, | ||
215 | IRLMP_EVENT event) | ||
216 | { | ||
217 | struct lsap_cb *lsap; | ||
218 | struct lsap_cb *lsap_next; | ||
219 | |||
220 | /* Note : this function use the new hashbin_find_next() | ||
221 | * function, instead of the old hashbin_get_next(). | ||
222 | * This make sure that we are always pointing one lsap | ||
223 | * ahead, so that if the current lsap is removed as the | ||
224 | * result of sending the event, we don't care. | ||
225 | * Also, as we store the context ourselves, if an enumeration | ||
226 | * of the same lsap hashbin happens as the result of sending the | ||
227 | * event, we don't care. | ||
228 | * The only problem is if the next lsap is removed. In that case, | ||
229 | * hashbin_find_next() will return NULL and we will abort the | ||
230 | * enumeration. - Jean II */ | ||
231 | |||
232 | /* Also : we don't accept any skb in input. We can *NOT* pass | ||
233 | * the same skb to multiple clients safely, we would need to | ||
234 | * skb_clone() it. - Jean II */ | ||
235 | |||
236 | lsap = (struct lsap_cb *) hashbin_get_first(lsap_hashbin); | ||
237 | |||
238 | while (NULL != hashbin_find_next(lsap_hashbin, | ||
239 | (long) lsap, | ||
240 | NULL, | ||
241 | (void *) &lsap_next) ) { | ||
242 | irlmp_do_lsap_event(lsap, event, NULL); | ||
243 | lsap = lsap_next; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | /********************************************************************* | ||
248 | * | ||
249 | * LAP connection control states | ||
250 | * | ||
251 | ********************************************************************/ | ||
252 | |||
253 | /* | ||
254 | * Function irlmp_state_standby (event, skb, info) | ||
255 | * | ||
256 | * STANDBY, The IrLAP connection does not exist. | ||
257 | * | ||
258 | */ | ||
259 | static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event, | ||
260 | struct sk_buff *skb) | ||
261 | { | ||
262 | IRDA_DEBUG(4, "%s()\n", __FUNCTION__); | ||
263 | IRDA_ASSERT(self->irlap != NULL, return;); | ||
264 | |||
265 | switch (event) { | ||
266 | case LM_LAP_DISCOVERY_REQUEST: | ||
267 | /* irlmp_next_station_state( LMP_DISCOVER); */ | ||
268 | |||
269 | irlap_discovery_request(self->irlap, &irlmp->discovery_cmd); | ||
270 | break; | ||
271 | case LM_LAP_CONNECT_INDICATION: | ||
272 | /* It's important to switch state first, to avoid IrLMP to | ||
273 | * think that the link is free since IrLMP may then start | ||
274 | * discovery before the connection is properly set up. DB. | ||
275 | */ | ||
276 | irlmp_next_lap_state(self, LAP_ACTIVE); | ||
277 | |||
278 | /* Just accept connection TODO, this should be fixed */ | ||
279 | irlap_connect_response(self->irlap, skb); | ||
280 | break; | ||
281 | case LM_LAP_CONNECT_REQUEST: | ||
282 | IRDA_DEBUG(4, "%s() LS_CONNECT_REQUEST\n", __FUNCTION__); | ||
283 | |||
284 | irlmp_next_lap_state(self, LAP_U_CONNECT); | ||
285 | |||
286 | /* FIXME: need to set users requested QoS */ | ||
287 | irlap_connect_request(self->irlap, self->daddr, NULL, 0); | ||
288 | break; | ||
289 | case LM_LAP_DISCONNECT_INDICATION: | ||
290 | IRDA_DEBUG(4, "%s(), Error LM_LAP_DISCONNECT_INDICATION\n", | ||
291 | __FUNCTION__); | ||
292 | |||
293 | irlmp_next_lap_state(self, LAP_STANDBY); | ||
294 | break; | ||
295 | default: | ||
296 | IRDA_DEBUG(0, "%s(), Unknown event %s\n", | ||
297 | __FUNCTION__, irlmp_event[event]); | ||
298 | break; | ||
299 | } | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * Function irlmp_state_u_connect (event, skb, info) | ||
304 | * | ||
305 | * U_CONNECT, The layer above has tried to open an LSAP connection but | ||
306 | * since the IrLAP connection does not exist, we must first start an | ||
307 | * IrLAP connection. We are now waiting response from IrLAP. | ||
308 | * */ | ||
309 | static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event, | ||
310 | struct sk_buff *skb) | ||
311 | { | ||
312 | IRDA_DEBUG(2, "%s(), event=%s\n", __FUNCTION__, irlmp_event[event]); | ||
313 | |||
314 | switch (event) { | ||
315 | case LM_LAP_CONNECT_INDICATION: | ||
316 | /* It's important to switch state first, to avoid IrLMP to | ||
317 | * think that the link is free since IrLMP may then start | ||
318 | * discovery before the connection is properly set up. DB. | ||
319 | */ | ||
320 | irlmp_next_lap_state(self, LAP_ACTIVE); | ||
321 | |||
322 | /* Just accept connection TODO, this should be fixed */ | ||
323 | irlap_connect_response(self->irlap, skb); | ||
324 | |||
325 | /* Tell LSAPs that they can start sending data */ | ||
326 | irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM); | ||
327 | |||
328 | /* Note : by the time we get there (LAP retries and co), | ||
329 | * the lsaps may already have gone. This avoid getting stuck | ||
330 | * forever in LAP_ACTIVE state - Jean II */ | ||
331 | if (HASHBIN_GET_SIZE(self->lsaps) == 0) { | ||
332 | IRDA_DEBUG(0, "%s() NO LSAPs !\n", __FUNCTION__); | ||
333 | irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT); | ||
334 | } | ||
335 | break; | ||
336 | case LM_LAP_CONNECT_REQUEST: | ||
337 | /* Already trying to connect */ | ||
338 | break; | ||
339 | case LM_LAP_CONNECT_CONFIRM: | ||
340 | /* For all lsap_ce E Associated do LS_Connect_confirm */ | ||
341 | irlmp_next_lap_state(self, LAP_ACTIVE); | ||
342 | |||
343 | /* Tell LSAPs that they can start sending data */ | ||
344 | irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM); | ||
345 | |||
346 | /* Note : by the time we get there (LAP retries and co), | ||
347 | * the lsaps may already have gone. This avoid getting stuck | ||
348 | * forever in LAP_ACTIVE state - Jean II */ | ||
349 | if (HASHBIN_GET_SIZE(self->lsaps) == 0) { | ||
350 | IRDA_DEBUG(0, "%s() NO LSAPs !\n", __FUNCTION__); | ||
351 | irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT); | ||
352 | } | ||
353 | break; | ||
354 | case LM_LAP_DISCONNECT_INDICATION: | ||
355 | IRDA_DEBUG(4, "%s(), LM_LAP_DISCONNECT_INDICATION\n", __FUNCTION__); | ||
356 | irlmp_next_lap_state(self, LAP_STANDBY); | ||
357 | |||
358 | /* Send disconnect event to all LSAPs using this link */ | ||
359 | irlmp_do_all_lsap_event(self->lsaps, | ||
360 | LM_LAP_DISCONNECT_INDICATION); | ||
361 | break; | ||
362 | case LM_LAP_DISCONNECT_REQUEST: | ||
363 | IRDA_DEBUG(4, "%s(), LM_LAP_DISCONNECT_REQUEST\n", __FUNCTION__); | ||
364 | |||
365 | /* One of the LSAP did timeout or was closed, if it was | ||
366 | * the last one, try to get out of here - Jean II */ | ||
367 | if (HASHBIN_GET_SIZE(self->lsaps) <= 1) { | ||
368 | irlap_disconnect_request(self->irlap); | ||
369 | } | ||
370 | break; | ||
371 | default: | ||
372 | IRDA_DEBUG(0, "%s(), Unknown event %s\n", | ||
373 | __FUNCTION__, irlmp_event[event]); | ||
374 | break; | ||
375 | } | ||
376 | } | ||
377 | |||
378 | /* | ||
379 | * Function irlmp_state_active (event, skb, info) | ||
380 | * | ||
381 | * ACTIVE, IrLAP connection is active | ||
382 | * | ||
383 | */ | ||
384 | static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event, | ||
385 | struct sk_buff *skb) | ||
386 | { | ||
387 | IRDA_DEBUG(4, "%s()\n", __FUNCTION__); | ||
388 | |||
389 | switch (event) { | ||
390 | case LM_LAP_CONNECT_REQUEST: | ||
391 | IRDA_DEBUG(4, "%s(), LS_CONNECT_REQUEST\n", __FUNCTION__); | ||
392 | |||
393 | /* | ||
394 | * IrLAP may have a pending disconnect. We tried to close | ||
395 | * IrLAP, but it was postponed because the link was | ||
396 | * busy or we were still sending packets. As we now | ||
397 | * need it, make sure it stays on. Jean II | ||
398 | */ | ||
399 | irlap_clear_disconnect(self->irlap); | ||
400 | |||
401 | /* | ||
402 | * LAP connection already active, just bounce back! Since we | ||
403 | * don't know which LSAP that tried to do this, we have to | ||
404 | * notify all LSAPs using this LAP, but that should be safe to | ||
405 | * do anyway. | ||
406 | */ | ||
407 | irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM); | ||
408 | |||
409 | /* Needed by connect indication */ | ||
410 | irlmp_do_all_lsap_event(irlmp->unconnected_lsaps, | ||
411 | LM_LAP_CONNECT_CONFIRM); | ||
412 | /* Keep state */ | ||
413 | break; | ||
414 | case LM_LAP_DISCONNECT_REQUEST: | ||
415 | /* | ||
416 | * Need to find out if we should close IrLAP or not. If there | ||
417 | * is only one LSAP connection left on this link, that LSAP | ||
418 | * must be the one that tries to close IrLAP. It will be | ||
419 | * removed later and moved to the list of unconnected LSAPs | ||
420 | */ | ||
421 | if (HASHBIN_GET_SIZE(self->lsaps) > 0) { | ||
422 | /* Timer value is checked in irsysctl - Jean II */ | ||
423 | irlmp_start_idle_timer(self, sysctl_lap_keepalive_time * HZ / 1000); | ||
424 | } else { | ||
425 | /* No more connections, so close IrLAP */ | ||
426 | |||
427 | /* We don't want to change state just yet, because | ||
428 | * we want to reflect accurately the real state of | ||
429 | * the LAP, not the state we wish it was in, | ||
430 | * so that we don't lose LM_LAP_CONNECT_REQUEST. | ||
431 | * In some cases, IrLAP won't close the LAP | ||
432 | * immediately. For example, it might still be | ||
433 | * retrying packets or waiting for the pf bit. | ||
434 | * As the LAP always send a DISCONNECT_INDICATION | ||
435 | * in PCLOSE or SCLOSE, just change state on that. | ||
436 | * Jean II */ | ||
437 | irlap_disconnect_request(self->irlap); | ||
438 | } | ||
439 | break; | ||
440 | case LM_LAP_IDLE_TIMEOUT: | ||
441 | if (HASHBIN_GET_SIZE(self->lsaps) == 0) { | ||
442 | /* Same reasoning as above - keep state */ | ||
443 | irlap_disconnect_request(self->irlap); | ||
444 | } | ||
445 | break; | ||
446 | case LM_LAP_DISCONNECT_INDICATION: | ||
447 | irlmp_next_lap_state(self, LAP_STANDBY); | ||
448 | |||
449 | /* In some case, at this point our side has already closed | ||
450 | * all lsaps, and we are waiting for the idle_timer to | ||
451 | * expire. If another device reconnect immediately, the | ||
452 | * idle timer will expire in the midle of the connection | ||
453 | * initialisation, screwing up things a lot... | ||
454 | * Therefore, we must stop the timer... */ | ||
455 | irlmp_stop_idle_timer(self); | ||
456 | |||
457 | /* | ||
458 | * Inform all connected LSAP's using this link | ||
459 | */ | ||
460 | irlmp_do_all_lsap_event(self->lsaps, | ||
461 | LM_LAP_DISCONNECT_INDICATION); | ||
462 | |||
463 | /* Force an expiry of the discovery log. | ||
464 | * Now that the LAP is free, the system may attempt to | ||
465 | * connect to another device. Unfortunately, our entries | ||
466 | * are stale. There is a small window (<3s) before the | ||
467 | * normal discovery will run and where irlmp_connect_request() | ||
468 | * can get the wrong info, so make sure things get | ||
469 | * cleaned *NOW* ;-) - Jean II */ | ||
470 | irlmp_do_expiry(); | ||
471 | break; | ||
472 | default: | ||
473 | IRDA_DEBUG(0, "%s(), Unknown event %s\n", | ||
474 | __FUNCTION__, irlmp_event[event]); | ||
475 | break; | ||
476 | } | ||
477 | } | ||
478 | |||
479 | /********************************************************************* | ||
480 | * | ||
481 | * LSAP connection control states | ||
482 | * | ||
483 | ********************************************************************/ | ||
484 | |||
485 | /* | ||
486 | * Function irlmp_state_disconnected (event, skb, info) | ||
487 | * | ||
488 | * DISCONNECTED | ||
489 | * | ||
490 | */ | ||
491 | static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event, | ||
492 | struct sk_buff *skb) | ||
493 | { | ||
494 | int ret = 0; | ||
495 | |||
496 | IRDA_DEBUG(4, "%s()\n", __FUNCTION__); | ||
497 | |||
498 | IRDA_ASSERT(self != NULL, return -1;); | ||
499 | IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); | ||
500 | |||
501 | switch (event) { | ||
502 | #ifdef CONFIG_IRDA_ULTRA | ||
503 | case LM_UDATA_INDICATION: | ||
504 | /* This is most bizzare. Those packets are aka unreliable | ||
505 | * connected, aka IrLPT or SOCK_DGRAM/IRDAPROTO_UNITDATA. | ||
506 | * Why do we pass them as Ultra ??? Jean II */ | ||
507 | irlmp_connless_data_indication(self, skb); | ||
508 | break; | ||
509 | #endif /* CONFIG_IRDA_ULTRA */ | ||
510 | case LM_CONNECT_REQUEST: | ||
511 | IRDA_DEBUG(4, "%s(), LM_CONNECT_REQUEST\n", __FUNCTION__); | ||
512 | |||
513 | if (self->conn_skb) { | ||
514 | IRDA_WARNING("%s: busy with another request!\n", | ||
515 | __FUNCTION__); | ||
516 | return -EBUSY; | ||
517 | } | ||
518 | /* Don't forget to refcount it (see irlmp_connect_request()) */ | ||
519 | skb_get(skb); | ||
520 | self->conn_skb = skb; | ||
521 | |||
522 | irlmp_next_lsap_state(self, LSAP_SETUP_PEND); | ||
523 | |||
524 | /* Start watchdog timer (5 secs for now) */ | ||
525 | irlmp_start_watchdog_timer(self, 5*HZ); | ||
526 | |||
527 | irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL); | ||
528 | break; | ||
529 | case LM_CONNECT_INDICATION: | ||
530 | if (self->conn_skb) { | ||
531 | IRDA_WARNING("%s: busy with another request!\n", | ||
532 | __FUNCTION__); | ||
533 | return -EBUSY; | ||
534 | } | ||
535 | /* Don't forget to refcount it (see irlap_driver_rcv()) */ | ||
536 | skb_get(skb); | ||
537 | self->conn_skb = skb; | ||
538 | |||
539 | irlmp_next_lsap_state(self, LSAP_CONNECT_PEND); | ||
540 | |||
541 | /* Start watchdog timer | ||
542 | * This is not mentionned in the spec, but there is a rare | ||
543 | * race condition that can get the socket stuck. | ||
544 | * If we receive this event while our LAP is closing down, | ||
545 | * the LM_LAP_CONNECT_REQUEST get lost and we get stuck in | ||
546 | * CONNECT_PEND state forever. | ||
547 | * The other cause of getting stuck down there is if the | ||
548 | * higher layer never reply to the CONNECT_INDICATION. | ||
549 | * Anyway, it make sense to make sure that we always have | ||
550 | * a backup plan. 1 second is plenty (should be immediate). | ||
551 | * Jean II */ | ||
552 | irlmp_start_watchdog_timer(self, 1*HZ); | ||
553 | |||
554 | irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL); | ||
555 | break; | ||
556 | default: | ||
557 | IRDA_DEBUG(1, "%s(), Unknown event %s on LSAP %#02x\n", | ||
558 | __FUNCTION__, irlmp_event[event], self->slsap_sel); | ||
559 | break; | ||
560 | } | ||
561 | return ret; | ||
562 | } | ||
563 | |||
564 | /* | ||
565 | * Function irlmp_state_connect (self, event, skb) | ||
566 | * | ||
567 | * CONNECT | ||
568 | * | ||
569 | */ | ||
570 | static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event, | ||
571 | struct sk_buff *skb) | ||
572 | { | ||
573 | struct lsap_cb *lsap; | ||
574 | int ret = 0; | ||
575 | |||
576 | IRDA_DEBUG(4, "%s()\n", __FUNCTION__); | ||
577 | |||
578 | IRDA_ASSERT(self != NULL, return -1;); | ||
579 | IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); | ||
580 | |||
581 | switch (event) { | ||
582 | case LM_CONNECT_RESPONSE: | ||
583 | /* | ||
584 | * Bind this LSAP to the IrLAP link where the connect was | ||
585 | * received | ||
586 | */ | ||
587 | lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, | ||
588 | NULL); | ||
589 | |||
590 | IRDA_ASSERT(lsap == self, return -1;); | ||
591 | IRDA_ASSERT(self->lap != NULL, return -1;); | ||
592 | IRDA_ASSERT(self->lap->lsaps != NULL, return -1;); | ||
593 | |||
594 | hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, | ||
595 | (long) self, NULL); | ||
596 | |||
597 | set_bit(0, &self->connected); /* TRUE */ | ||
598 | |||
599 | irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, | ||
600 | self->slsap_sel, CONNECT_CNF, skb); | ||
601 | |||
602 | del_timer(&self->watchdog_timer); | ||
603 | |||
604 | irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY); | ||
605 | break; | ||
606 | case LM_WATCHDOG_TIMEOUT: | ||
607 | /* May happen, who knows... | ||
608 | * Jean II */ | ||
609 | IRDA_DEBUG(0, "%s() WATCHDOG_TIMEOUT!\n", __FUNCTION__); | ||
610 | |||
611 | /* Disconnect, get out... - Jean II */ | ||
612 | self->lap = NULL; | ||
613 | self->dlsap_sel = LSAP_ANY; | ||
614 | irlmp_next_lsap_state(self, LSAP_DISCONNECTED); | ||
615 | break; | ||
616 | default: | ||
617 | /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we | ||
618 | * are *not* yet bound to the IrLAP link. Jean II */ | ||
619 | IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n", | ||
620 | __FUNCTION__, irlmp_event[event], self->slsap_sel); | ||
621 | break; | ||
622 | } | ||
623 | return ret; | ||
624 | } | ||
625 | |||
626 | /* | ||
627 | * Function irlmp_state_connect_pend (event, skb, info) | ||
628 | * | ||
629 | * CONNECT_PEND | ||
630 | * | ||
631 | */ | ||
632 | static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event, | ||
633 | struct sk_buff *skb) | ||
634 | { | ||
635 | struct sk_buff *tx_skb; | ||
636 | int ret = 0; | ||
637 | |||
638 | IRDA_DEBUG(4, "%s()\n", __FUNCTION__); | ||
639 | |||
640 | IRDA_ASSERT(self != NULL, return -1;); | ||
641 | IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); | ||
642 | |||
643 | switch (event) { | ||
644 | case LM_CONNECT_REQUEST: | ||
645 | /* Keep state */ | ||
646 | break; | ||
647 | case LM_CONNECT_RESPONSE: | ||
648 | IRDA_DEBUG(0, "%s(), LM_CONNECT_RESPONSE, " | ||
649 | "no indication issued yet\n", __FUNCTION__); | ||
650 | /* Keep state */ | ||
651 | break; | ||
652 | case LM_DISCONNECT_REQUEST: | ||
653 | IRDA_DEBUG(0, "%s(), LM_DISCONNECT_REQUEST, " | ||
654 | "not yet bound to IrLAP connection\n", __FUNCTION__); | ||
655 | /* Keep state */ | ||
656 | break; | ||
657 | case LM_LAP_CONNECT_CONFIRM: | ||
658 | IRDA_DEBUG(4, "%s(), LS_CONNECT_CONFIRM\n", __FUNCTION__); | ||
659 | irlmp_next_lsap_state(self, LSAP_CONNECT); | ||
660 | |||
661 | tx_skb = self->conn_skb; | ||
662 | self->conn_skb = NULL; | ||
663 | |||
664 | irlmp_connect_indication(self, tx_skb); | ||
665 | /* Drop reference count - see irlmp_connect_indication(). */ | ||
666 | dev_kfree_skb(tx_skb); | ||
667 | break; | ||
668 | case LM_WATCHDOG_TIMEOUT: | ||
669 | /* Will happen in some rare cases because of a race condition. | ||
670 | * Just make sure we don't stay there forever... | ||
671 | * Jean II */ | ||
672 | IRDA_DEBUG(0, "%s() WATCHDOG_TIMEOUT!\n", __FUNCTION__); | ||
673 | |||
674 | /* Go back to disconnected mode, keep the socket waiting */ | ||
675 | self->lap = NULL; | ||
676 | self->dlsap_sel = LSAP_ANY; | ||
677 | if(self->conn_skb) | ||
678 | dev_kfree_skb(self->conn_skb); | ||
679 | self->conn_skb = NULL; | ||
680 | irlmp_next_lsap_state(self, LSAP_DISCONNECTED); | ||
681 | break; | ||
682 | default: | ||
683 | /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we | ||
684 | * are *not* yet bound to the IrLAP link. Jean II */ | ||
685 | IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n", | ||
686 | __FUNCTION__, irlmp_event[event], self->slsap_sel); | ||
687 | break; | ||
688 | } | ||
689 | return ret; | ||
690 | } | ||
691 | |||
692 | /* | ||
693 | * Function irlmp_state_dtr (self, event, skb) | ||
694 | * | ||
695 | * DATA_TRANSFER_READY | ||
696 | * | ||
697 | */ | ||
698 | static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event, | ||
699 | struct sk_buff *skb) | ||
700 | { | ||
701 | LM_REASON reason; | ||
702 | int ret = 0; | ||
703 | |||
704 | IRDA_DEBUG(4, "%s()\n", __FUNCTION__); | ||
705 | |||
706 | IRDA_ASSERT(self != NULL, return -1;); | ||
707 | IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); | ||
708 | IRDA_ASSERT(self->lap != NULL, return -1;); | ||
709 | |||
710 | switch (event) { | ||
711 | case LM_DATA_REQUEST: /* Optimize for the common case */ | ||
712 | irlmp_send_data_pdu(self->lap, self->dlsap_sel, | ||
713 | self->slsap_sel, FALSE, skb); | ||
714 | break; | ||
715 | case LM_DATA_INDICATION: /* Optimize for the common case */ | ||
716 | irlmp_data_indication(self, skb); | ||
717 | break; | ||
718 | case LM_UDATA_REQUEST: | ||
719 | IRDA_ASSERT(skb != NULL, return -1;); | ||
720 | irlmp_send_data_pdu(self->lap, self->dlsap_sel, | ||
721 | self->slsap_sel, TRUE, skb); | ||
722 | break; | ||
723 | case LM_UDATA_INDICATION: | ||
724 | irlmp_udata_indication(self, skb); | ||
725 | break; | ||
726 | case LM_CONNECT_REQUEST: | ||
727 | IRDA_DEBUG(0, "%s(), LM_CONNECT_REQUEST, " | ||
728 | "error, LSAP already connected\n", __FUNCTION__); | ||
729 | /* Keep state */ | ||
730 | break; | ||
731 | case LM_CONNECT_RESPONSE: | ||
732 | IRDA_DEBUG(0, "%s(), LM_CONNECT_RESPONSE, " | ||
733 | "error, LSAP already connected\n", __FUNCTION__); | ||
734 | /* Keep state */ | ||
735 | break; | ||
736 | case LM_DISCONNECT_REQUEST: | ||
737 | irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, self->slsap_sel, | ||
738 | DISCONNECT, skb); | ||
739 | irlmp_next_lsap_state(self, LSAP_DISCONNECTED); | ||
740 | /* Called only from irlmp_disconnect_request(), will | ||
741 | * unbind from LAP over there. Jean II */ | ||
742 | |||
743 | /* Try to close the LAP connection if its still there */ | ||
744 | if (self->lap) { | ||
745 | IRDA_DEBUG(4, "%s(), trying to close IrLAP\n", | ||
746 | __FUNCTION__); | ||
747 | irlmp_do_lap_event(self->lap, | ||
748 | LM_LAP_DISCONNECT_REQUEST, | ||
749 | NULL); | ||
750 | } | ||
751 | break; | ||
752 | case LM_LAP_DISCONNECT_INDICATION: | ||
753 | irlmp_next_lsap_state(self, LSAP_DISCONNECTED); | ||
754 | |||
755 | reason = irlmp_convert_lap_reason(self->lap->reason); | ||
756 | |||
757 | irlmp_disconnect_indication(self, reason, NULL); | ||
758 | break; | ||
759 | case LM_DISCONNECT_INDICATION: | ||
760 | irlmp_next_lsap_state(self, LSAP_DISCONNECTED); | ||
761 | |||
762 | IRDA_ASSERT(self->lap != NULL, return -1;); | ||
763 | IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); | ||
764 | |||
765 | IRDA_ASSERT(skb != NULL, return -1;); | ||
766 | IRDA_ASSERT(skb->len > 3, return -1;); | ||
767 | reason = skb->data[3]; | ||
768 | |||
769 | /* Try to close the LAP connection */ | ||
770 | IRDA_DEBUG(4, "%s(), trying to close IrLAP\n", __FUNCTION__); | ||
771 | irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); | ||
772 | |||
773 | irlmp_disconnect_indication(self, reason, skb); | ||
774 | break; | ||
775 | default: | ||
776 | IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n", | ||
777 | __FUNCTION__, irlmp_event[event], self->slsap_sel); | ||
778 | break; | ||
779 | } | ||
780 | return ret; | ||
781 | } | ||
782 | |||
783 | /* | ||
784 | * Function irlmp_state_setup (event, skb, info) | ||
785 | * | ||
786 | * SETUP, Station Control has set up the underlying IrLAP connection. | ||
787 | * An LSAP connection request has been transmitted to the peer | ||
788 | * LSAP-Connection Control FSM and we are awaiting reply. | ||
789 | */ | ||
790 | static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event, | ||
791 | struct sk_buff *skb) | ||
792 | { | ||
793 | LM_REASON reason; | ||
794 | int ret = 0; | ||
795 | |||
796 | IRDA_ASSERT(self != NULL, return -1;); | ||
797 | IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); | ||
798 | |||
799 | IRDA_DEBUG(4, "%s()\n", __FUNCTION__); | ||
800 | |||
801 | switch (event) { | ||
802 | case LM_CONNECT_CONFIRM: | ||
803 | irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY); | ||
804 | |||
805 | del_timer(&self->watchdog_timer); | ||
806 | |||
807 | irlmp_connect_confirm(self, skb); | ||
808 | break; | ||
809 | case LM_DISCONNECT_INDICATION: | ||
810 | irlmp_next_lsap_state(self, LSAP_DISCONNECTED); | ||
811 | |||
812 | IRDA_ASSERT(self->lap != NULL, return -1;); | ||
813 | IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); | ||
814 | |||
815 | IRDA_ASSERT(skb != NULL, return -1;); | ||
816 | IRDA_ASSERT(skb->len > 3, return -1;); | ||
817 | reason = skb->data[3]; | ||
818 | |||
819 | /* Try to close the LAP connection */ | ||
820 | IRDA_DEBUG(4, "%s(), trying to close IrLAP\n", __FUNCTION__); | ||
821 | irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); | ||
822 | |||
823 | irlmp_disconnect_indication(self, reason, skb); | ||
824 | break; | ||
825 | case LM_LAP_DISCONNECT_INDICATION: | ||
826 | irlmp_next_lsap_state(self, LSAP_DISCONNECTED); | ||
827 | |||
828 | del_timer(&self->watchdog_timer); | ||
829 | |||
830 | IRDA_ASSERT(self->lap != NULL, return -1;); | ||
831 | IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); | ||
832 | |||
833 | reason = irlmp_convert_lap_reason(self->lap->reason); | ||
834 | |||
835 | irlmp_disconnect_indication(self, reason, skb); | ||
836 | break; | ||
837 | case LM_WATCHDOG_TIMEOUT: | ||
838 | IRDA_DEBUG(0, "%s() WATCHDOG_TIMEOUT!\n", __FUNCTION__); | ||
839 | |||
840 | IRDA_ASSERT(self->lap != NULL, return -1;); | ||
841 | irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); | ||
842 | irlmp_next_lsap_state(self, LSAP_DISCONNECTED); | ||
843 | |||
844 | irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL); | ||
845 | break; | ||
846 | default: | ||
847 | IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n", | ||
848 | __FUNCTION__, irlmp_event[event], self->slsap_sel); | ||
849 | break; | ||
850 | } | ||
851 | return ret; | ||
852 | } | ||
853 | |||
854 | /* | ||
855 | * Function irlmp_state_setup_pend (event, skb, info) | ||
856 | * | ||
857 | * SETUP_PEND, An LM_CONNECT_REQUEST has been received from the service | ||
858 | * user to set up an LSAP connection. A request has been sent to the | ||
859 | * LAP FSM to set up the underlying IrLAP connection, and we | ||
860 | * are awaiting confirm. | ||
861 | */ | ||
862 | static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event, | ||
863 | struct sk_buff *skb) | ||
864 | { | ||
865 | struct sk_buff *tx_skb; | ||
866 | LM_REASON reason; | ||
867 | int ret = 0; | ||
868 | |||
869 | IRDA_DEBUG(4, "%s()\n", __FUNCTION__); | ||
870 | |||
871 | IRDA_ASSERT(self != NULL, return -1;); | ||
872 | IRDA_ASSERT(irlmp != NULL, return -1;); | ||
873 | |||
874 | switch (event) { | ||
875 | case LM_LAP_CONNECT_CONFIRM: | ||
876 | IRDA_ASSERT(self->conn_skb != NULL, return -1;); | ||
877 | |||
878 | tx_skb = self->conn_skb; | ||
879 | self->conn_skb = NULL; | ||
880 | |||
881 | irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, | ||
882 | self->slsap_sel, CONNECT_CMD, tx_skb); | ||
883 | /* Drop reference count - see irlap_data_request(). */ | ||
884 | dev_kfree_skb(tx_skb); | ||
885 | |||
886 | irlmp_next_lsap_state(self, LSAP_SETUP); | ||
887 | break; | ||
888 | case LM_WATCHDOG_TIMEOUT: | ||
889 | IRDA_DEBUG(0, "%s() : WATCHDOG_TIMEOUT !\n", __FUNCTION__); | ||
890 | |||
891 | IRDA_ASSERT(self->lap != NULL, return -1;); | ||
892 | irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); | ||
893 | irlmp_next_lsap_state(self, LSAP_DISCONNECTED); | ||
894 | |||
895 | irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL); | ||
896 | break; | ||
897 | case LM_LAP_DISCONNECT_INDICATION: /* LS_Disconnect.indication */ | ||
898 | del_timer( &self->watchdog_timer); | ||
899 | |||
900 | irlmp_next_lsap_state(self, LSAP_DISCONNECTED); | ||
901 | |||
902 | reason = irlmp_convert_lap_reason(self->lap->reason); | ||
903 | |||
904 | irlmp_disconnect_indication(self, reason, NULL); | ||
905 | break; | ||
906 | default: | ||
907 | IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n", | ||
908 | __FUNCTION__, irlmp_event[event], self->slsap_sel); | ||
909 | break; | ||
910 | } | ||
911 | return ret; | ||
912 | } | ||