[IRDA]: IrCOMM discovery indication simplification
[pandora-kernel.git] / net / irda / ircomm / ircomm_tty_attach.c
1 /*********************************************************************
2  *
3  * Filename:      ircomm_tty_attach.c
4  * Version:
5  * Description:   Code for attaching the serial driver to IrCOMM
6  * Status:        Experimental.
7  * Author:        Dag Brattli <dagb@cs.uit.no>
8  * Created at:    Sat Jun  5 17:42:00 1999
9  * Modified at:   Tue Jan  4 14:20:49 2000
10  * Modified by:   Dag Brattli <dagb@cs.uit.no>
11  *
12  *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
13  *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
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  *     This program is distributed in the hope that it will be useful,
21  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23  *     GNU General Public License for more details.
24  *
25  *     You should have received a copy of the GNU General Public License
26  *     along with this program; if not, write to the Free Software
27  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston,
28  *     MA 02111-1307 USA
29  *
30  ********************************************************************/
31
32 #include <linux/init.h>
33
34 #include <net/irda/irda.h>
35 #include <net/irda/irlmp.h>
36 #include <net/irda/iriap.h>
37 #include <net/irda/irttp.h>
38 #include <net/irda/irias_object.h>
39 #include <net/irda/parameters.h>
40
41 #include <net/irda/ircomm_core.h>
42 #include <net/irda/ircomm_param.h>
43 #include <net/irda/ircomm_event.h>
44
45 #include <net/irda/ircomm_tty.h>
46 #include <net/irda/ircomm_tty_attach.h>
47
48 static void ircomm_tty_ias_register(struct ircomm_tty_cb *self);
49 static void ircomm_tty_discovery_indication(discinfo_t *discovery,
50                                             DISCOVERY_MODE mode,
51                                             void *priv);
52 static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
53                                         struct ias_value *value, void *priv);
54 static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
55                                             int timeout);
56 static void ircomm_tty_watchdog_timer_expired(void *data);
57
58 static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
59                                  IRCOMM_TTY_EVENT event,
60                                  struct sk_buff *skb,
61                                  struct ircomm_tty_info *info);
62 static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
63                                    IRCOMM_TTY_EVENT event,
64                                    struct sk_buff *skb,
65                                    struct ircomm_tty_info *info);
66 static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
67                                              IRCOMM_TTY_EVENT event,
68                                              struct sk_buff *skb,
69                                              struct ircomm_tty_info *info);
70 static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
71                                            IRCOMM_TTY_EVENT event,
72                                            struct sk_buff *skb,
73                                            struct ircomm_tty_info *info);
74 static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
75                                   IRCOMM_TTY_EVENT event,
76                                   struct sk_buff *skb,
77                                   struct ircomm_tty_info *info);
78 static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
79                                   IRCOMM_TTY_EVENT event,
80                                   struct sk_buff *skb,
81                                   struct ircomm_tty_info *info);
82
83 char *ircomm_tty_state[] = {
84         "IRCOMM_TTY_IDLE",
85         "IRCOMM_TTY_SEARCH",
86         "IRCOMM_TTY_QUERY_PARAMETERS",
87         "IRCOMM_TTY_QUERY_LSAP_SEL",
88         "IRCOMM_TTY_SETUP",
89         "IRCOMM_TTY_READY",
90         "*** ERROR *** ",
91 };
92
93 #ifdef CONFIG_IRDA_DEBUG
94 static char *ircomm_tty_event[] = {
95         "IRCOMM_TTY_ATTACH_CABLE",
96         "IRCOMM_TTY_DETACH_CABLE",
97         "IRCOMM_TTY_DATA_REQUEST",
98         "IRCOMM_TTY_DATA_INDICATION",
99         "IRCOMM_TTY_DISCOVERY_REQUEST",
100         "IRCOMM_TTY_DISCOVERY_INDICATION",
101         "IRCOMM_TTY_CONNECT_CONFIRM",
102         "IRCOMM_TTY_CONNECT_INDICATION",
103         "IRCOMM_TTY_DISCONNECT_REQUEST",
104         "IRCOMM_TTY_DISCONNECT_INDICATION",
105         "IRCOMM_TTY_WD_TIMER_EXPIRED",
106         "IRCOMM_TTY_GOT_PARAMETERS",
107         "IRCOMM_TTY_GOT_LSAPSEL",
108         "*** ERROR ****",
109 };
110 #endif /* CONFIG_IRDA_DEBUG */
111
112 static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
113                       struct sk_buff *skb, struct ircomm_tty_info *info) =
114 {
115         ircomm_tty_state_idle,
116         ircomm_tty_state_search,
117         ircomm_tty_state_query_parameters,
118         ircomm_tty_state_query_lsap_sel,
119         ircomm_tty_state_setup,
120         ircomm_tty_state_ready,
121 };
122
123 /*
124  * Function ircomm_tty_attach_cable (driver)
125  *
126  *    Try to attach cable (IrCOMM link). This function will only return
127  *    when the link has been connected, or if an error condition occurs.
128  *    If success, the return value is the resulting service type.
129  */
130 int ircomm_tty_attach_cable(struct ircomm_tty_cb *self)
131 {
132         IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
133
134         IRDA_ASSERT(self != NULL, return -1;);
135         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
136
137         /* Check if somebody has already connected to us */
138         if (ircomm_is_connected(self->ircomm)) {
139                 IRDA_DEBUG(0, "%s(), already connected!\n", __FUNCTION__ );
140                 return 0;
141         }
142
143         /* Make sure nobody tries to write before the link is up */
144         self->tty->hw_stopped = 1;
145
146         ircomm_tty_ias_register(self);
147
148         ircomm_tty_do_event(self, IRCOMM_TTY_ATTACH_CABLE, NULL, NULL);
149
150         return 0;
151 }
152
153 /*
154  * Function ircomm_detach_cable (driver)
155  *
156  *    Detach cable, or cable has been detached by peer
157  *
158  */
159 void ircomm_tty_detach_cable(struct ircomm_tty_cb *self)
160 {
161         IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
162
163         IRDA_ASSERT(self != NULL, return;);
164         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
165
166         del_timer(&self->watchdog_timer);
167
168         /* Remove discovery handler */
169         if (self->ckey) {
170                 irlmp_unregister_client(self->ckey);
171                 self->ckey = NULL;
172         }
173         /* Remove IrCOMM hint bits */
174         if (self->skey) {
175                 irlmp_unregister_service(self->skey);
176                 self->skey = NULL;
177         }
178
179         if (self->iriap) {
180                 iriap_close(self->iriap);
181                 self->iriap = NULL;
182         }
183
184         /* Remove LM-IAS object */
185         if (self->obj) {
186                 irias_delete_object(self->obj);
187                 self->obj = NULL;
188         }
189
190         ircomm_tty_do_event(self, IRCOMM_TTY_DETACH_CABLE, NULL, NULL);
191
192         /* Reset some values */
193         self->daddr = self->saddr = 0;
194         self->dlsap_sel = self->slsap_sel = 0;
195
196         memset(&self->settings, 0, sizeof(struct ircomm_params));
197 }
198
199 /*
200  * Function ircomm_tty_ias_register (self)
201  *
202  *    Register with LM-IAS depending on which service type we are
203  *
204  */
205 static void ircomm_tty_ias_register(struct ircomm_tty_cb *self)
206 {
207         __u8 oct_seq[6];
208         __u16 hints;
209
210         IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
211
212         IRDA_ASSERT(self != NULL, return;);
213         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
214
215         /* Compute hint bits based on service */
216         hints = irlmp_service_to_hint(S_COMM);
217         if (self->service_type & IRCOMM_3_WIRE_RAW)
218                 hints |= irlmp_service_to_hint(S_PRINTER);
219
220         /* Advertise IrCOMM hint bit in discovery */
221         if (!self->skey)
222                 self->skey = irlmp_register_service(hints);
223         /* Set up a discovery handler */
224         if (!self->ckey)
225                 self->ckey = irlmp_register_client(hints,
226                                                    ircomm_tty_discovery_indication,
227                                                    NULL, (void *) self);
228
229         /* If already done, no need to do it again */
230         if (self->obj)
231                 return;
232
233         if (self->service_type & IRCOMM_3_WIRE_RAW) {
234                 /* Register IrLPT with LM-IAS */
235                 self->obj = irias_new_object("IrLPT", IAS_IRLPT_ID);
236                 irias_add_integer_attrib(self->obj, "IrDA:IrLMP:LsapSel",
237                                          self->slsap_sel, IAS_KERNEL_ATTR);
238         } else {
239                 /* Register IrCOMM with LM-IAS */
240                 self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID);
241                 irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel",
242                                          self->slsap_sel, IAS_KERNEL_ATTR);
243
244                 /* Code the parameters into the buffer */
245                 irda_param_pack(oct_seq, "bbbbbb",
246                                 IRCOMM_SERVICE_TYPE, 1, self->service_type,
247                                 IRCOMM_PORT_TYPE,    1, IRCOMM_SERIAL);
248
249                 /* Register parameters with LM-IAS */
250                 irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6,
251                                         IAS_KERNEL_ATTR);
252         }
253         irias_insert_object(self->obj);
254 }
255
256 /*
257  * Function ircomm_tty_ias_unregister (self)
258  *
259  *    Remove our IAS object and client hook while connected.
260  *
261  */
262 static void ircomm_tty_ias_unregister(struct ircomm_tty_cb *self)
263 {
264         /* Remove LM-IAS object now so it is not reused.
265          * IrCOMM deals very poorly with multiple incoming connections.
266          * It should looks a lot more like IrNET, and "dup" a server TSAP
267          * to the application TSAP (based on various rules).
268          * This is a cheap workaround allowing multiple clients to
269          * connect to us. It will not always work.
270          * Each IrCOMM socket has an IAS entry. Incoming connection will
271          * pick the first one found. So, when we are fully connected,
272          * we remove our IAS entries so that the next IAS entry is used.
273          * We do that for *both* client and server, because a server
274          * can also create client instances.
275          * Jean II */
276         if (self->obj) {
277                 irias_delete_object(self->obj);
278                 self->obj = NULL;
279         }
280
281 #if 0
282         /* Remove discovery handler.
283          * While we are connected, we no longer need to receive
284          * discovery events. This would be the case if there is
285          * multiple IrLAP interfaces. Jean II */
286         if (self->ckey) {
287                 irlmp_unregister_client(self->ckey);
288                 self->ckey = NULL;
289         }
290 #endif
291 }
292
293 /*
294  * Function ircomm_send_initial_parameters (self)
295  *
296  *    Send initial parameters to the remote IrCOMM device. These parameters
297  *    must be sent before any data.
298  */
299 int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self)
300 {
301         IRDA_ASSERT(self != NULL, return -1;);
302         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
303
304         if (self->service_type & IRCOMM_3_WIRE_RAW)
305                 return 0;
306
307         /*
308          * Set default values, but only if the application for some reason
309          * haven't set them already
310          */
311         IRDA_DEBUG(2, "%s(), data-rate = %d\n", __FUNCTION__ ,
312                    self->settings.data_rate);
313         if (!self->settings.data_rate)
314                 self->settings.data_rate = 9600;
315         IRDA_DEBUG(2, "%s(), data-format = %d\n", __FUNCTION__ ,
316                    self->settings.data_format);
317         if (!self->settings.data_format)
318                 self->settings.data_format = IRCOMM_WSIZE_8;  /* 8N1 */
319
320         IRDA_DEBUG(2, "%s(), flow-control = %d\n", __FUNCTION__ ,
321                    self->settings.flow_control);
322         /*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/
323
324         /* Do not set delta values for the initial parameters */
325         self->settings.dte = IRCOMM_DTR | IRCOMM_RTS;
326
327         /* Only send service type parameter when we are the client */
328         if (self->client)
329                 ircomm_param_request(self, IRCOMM_SERVICE_TYPE, FALSE);
330         ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
331         ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
332
333         /* For a 3 wire service, we just flush the last parameter and return */
334         if (self->settings.service_type == IRCOMM_3_WIRE) {
335                 ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
336                 return 0;
337         }
338
339         /* Only 9-wire service types continue here */
340         ircomm_param_request(self, IRCOMM_FLOW_CONTROL, FALSE);
341 #if 0
342         ircomm_param_request(self, IRCOMM_XON_XOFF, FALSE);
343         ircomm_param_request(self, IRCOMM_ENQ_ACK, FALSE);
344 #endif
345         /* Notify peer that we are ready to receive data */
346         ircomm_param_request(self, IRCOMM_DTE, TRUE);
347
348         return 0;
349 }
350
351 /*
352  * Function ircomm_tty_discovery_indication (discovery)
353  *
354  *    Remote device is discovered, try query the remote IAS to see which
355  *    device it is, and which services it has.
356  *
357  */
358 static void ircomm_tty_discovery_indication(discinfo_t *discovery,
359                                             DISCOVERY_MODE mode,
360                                             void *priv)
361 {
362         struct ircomm_tty_cb *self;
363         struct ircomm_tty_info info;
364
365         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
366
367         /* Important note :
368          * We need to drop all passive discoveries.
369          * The LSAP management of IrComm is deficient and doesn't deal
370          * with the case of two instance connecting to each other
371          * simultaneously (it will deadlock in LMP).
372          * The proper fix would be to use the same technique as in IrNET,
373          * to have one server socket and separate instances for the
374          * connecting/connected socket.
375          * The workaround is to drop passive discovery, which drastically
376          * reduce the probability of this happening.
377          * Jean II */
378         if(mode == DISCOVERY_PASSIVE)
379                 return;
380
381         info.daddr = discovery->daddr;
382         info.saddr = discovery->saddr;
383
384         self = (struct ircomm_tty_cb *) priv;
385         ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION,
386                             NULL, &info);
387 }
388
389 /*
390  * Function ircomm_tty_disconnect_indication (instance, sap, reason, skb)
391  *
392  *    Link disconnected
393  *
394  */
395 void ircomm_tty_disconnect_indication(void *instance, void *sap,
396                                       LM_REASON reason,
397                                       struct sk_buff *skb)
398 {
399         struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
400
401         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
402
403         IRDA_ASSERT(self != NULL, return;);
404         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
405
406         if (!self->tty)
407                 return;
408
409         /* This will stop control data transfers */
410         self->flow = FLOW_STOP;
411
412         /* Stop data transfers */
413         self->tty->hw_stopped = 1;
414
415         ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL,
416                             NULL);
417 }
418
419 /*
420  * Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv)
421  *
422  *    Got result from the IAS query we make
423  *
424  */
425 static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
426                                         struct ias_value *value,
427                                         void *priv)
428 {
429         struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv;
430
431         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
432
433         IRDA_ASSERT(self != NULL, return;);
434         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
435
436         /* We probably don't need to make any more queries */
437         iriap_close(self->iriap);
438         self->iriap = NULL;
439
440         /* Check if request succeeded */
441         if (result != IAS_SUCCESS) {
442                 IRDA_DEBUG(4, "%s(), got NULL value!\n", __FUNCTION__ );
443                 return;
444         }
445
446         switch (value->type) {
447         case IAS_OCT_SEQ:
448                 IRDA_DEBUG(2, "%s(), got octet sequence\n", __FUNCTION__ );
449
450                 irda_param_extract_all(self, value->t.oct_seq, value->len,
451                                        &ircomm_param_info);
452
453                 ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL,
454                                     NULL);
455                 break;
456         case IAS_INTEGER:
457                 /* Got LSAP selector */
458                 IRDA_DEBUG(2, "%s(), got lsapsel = %d\n", __FUNCTION__ ,
459                            value->t.integer);
460
461                 if (value->t.integer == -1) {
462                         IRDA_DEBUG(0, "%s(), invalid value!\n", __FUNCTION__ );
463                 } else
464                         self->dlsap_sel = value->t.integer;
465
466                 ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL);
467                 break;
468         case IAS_MISSING:
469                 IRDA_DEBUG(0, "%s(), got IAS_MISSING\n", __FUNCTION__ );
470                 break;
471         default:
472                 IRDA_DEBUG(0, "%s(), got unknown type!\n", __FUNCTION__ );
473                 break;
474         }
475         irias_delete_value(value);
476 }
477
478 /*
479  * Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb)
480  *
481  *    Connection confirmed
482  *
483  */
484 void ircomm_tty_connect_confirm(void *instance, void *sap,
485                                 struct qos_info *qos,
486                                 __u32 max_data_size,
487                                 __u8 max_header_size,
488                                 struct sk_buff *skb)
489 {
490         struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
491
492         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
493
494         IRDA_ASSERT(self != NULL, return;);
495         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
496
497         self->client = TRUE;
498         self->max_data_size = max_data_size;
499         self->max_header_size = max_header_size;
500         self->flow = FLOW_START;
501
502         ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL);
503
504         /* No need to kfree_skb - see ircomm_ttp_connect_confirm() */
505 }
506
507 /*
508  * Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size,
509  *                                         skb)
510  *
511  *    we are discovered and being requested to connect by remote device !
512  *
513  */
514 void ircomm_tty_connect_indication(void *instance, void *sap,
515                                    struct qos_info *qos,
516                                    __u32 max_data_size,
517                                    __u8 max_header_size,
518                                    struct sk_buff *skb)
519 {
520         struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
521         int clen;
522
523         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
524
525         IRDA_ASSERT(self != NULL, return;);
526         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
527
528         self->client = FALSE;
529         self->max_data_size = max_data_size;
530         self->max_header_size = max_header_size;
531         self->flow = FLOW_START;
532
533         clen = skb->data[0];
534         if (clen)
535                 irda_param_extract_all(self, skb->data+1,
536                                        IRDA_MIN(skb->len, clen),
537                                        &ircomm_param_info);
538
539         ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL);
540
541         /* No need to kfree_skb - see ircomm_ttp_connect_indication() */
542 }
543
544 /*
545  * Function ircomm_tty_link_established (self)
546  *
547  *    Called when the IrCOMM link is established
548  *
549  */
550 void ircomm_tty_link_established(struct ircomm_tty_cb *self)
551 {
552         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
553
554         IRDA_ASSERT(self != NULL, return;);
555         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
556
557         if (!self->tty)
558                 return;
559
560         del_timer(&self->watchdog_timer);
561
562         /*
563          * IrCOMM link is now up, and if we are not using hardware
564          * flow-control, then declare the hardware as running. Otherwise we
565          * will have to wait for the peer device (DCE) to raise the CTS
566          * line.
567          */
568         if ((self->flags & ASYNC_CTS_FLOW) && ((self->settings.dce & IRCOMM_CTS) == 0)) {
569                 IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __FUNCTION__ );
570                 return;
571         } else {
572                 IRDA_DEBUG(1, "%s(), starting hardware!\n", __FUNCTION__ );
573
574                 self->tty->hw_stopped = 0;
575
576                 /* Wake up processes blocked on open */
577                 wake_up_interruptible(&self->open_wait);
578         }
579
580         schedule_work(&self->tqueue);
581 }
582
583 /*
584  * Function ircomm_tty_start_watchdog_timer (self, timeout)
585  *
586  *    Start the watchdog timer. This timer is used to make sure that any
587  *    connection attempt is successful, and if not, we will retry after
588  *    the timeout
589  */
590 static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
591                                             int timeout)
592 {
593         IRDA_ASSERT(self != NULL, return;);
594         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
595
596         irda_start_timer(&self->watchdog_timer, timeout, (void *) self,
597                          ircomm_tty_watchdog_timer_expired);
598 }
599
600 /*
601  * Function ircomm_tty_watchdog_timer_expired (data)
602  *
603  *    Called when the connect procedure have taken to much time.
604  *
605  */
606 static void ircomm_tty_watchdog_timer_expired(void *data)
607 {
608         struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data;
609
610         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
611
612         IRDA_ASSERT(self != NULL, return;);
613         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
614
615         ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL);
616 }
617
618
619 /*
620  * Function ircomm_tty_do_event (self, event, skb)
621  *
622  *    Process event
623  *
624  */
625 int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
626                         struct sk_buff *skb, struct ircomm_tty_info *info)
627 {
628         IRDA_ASSERT(self != NULL, return -1;);
629         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
630
631         IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ ,
632                    ircomm_tty_state[self->state], ircomm_tty_event[event]);
633
634         return (*state[self->state])(self, event, skb, info);
635 }
636
637 /*
638  * Function ircomm_tty_next_state (self, state)
639  *
640  *    Switch state
641  *
642  */
643 static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state)
644 {
645         /*
646         IRDA_ASSERT(self != NULL, return;);
647         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
648
649         IRDA_DEBUG(2, "%s: next state=%s, service type=%d\n", __FUNCTION__ ,
650                    ircomm_tty_state[self->state], self->service_type);
651         */
652         self->state = state;
653 }
654
655 /*
656  * Function ircomm_tty_state_idle (self, event, skb, info)
657  *
658  *    Just hanging around
659  *
660  */
661 static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
662                                  IRCOMM_TTY_EVENT event,
663                                  struct sk_buff *skb,
664                                  struct ircomm_tty_info *info)
665 {
666         int ret = 0;
667
668         IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ ,
669                    ircomm_tty_state[self->state], ircomm_tty_event[event]);
670         switch (event) {
671         case IRCOMM_TTY_ATTACH_CABLE:
672                 /* Try to discover any remote devices */
673                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
674                 ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
675
676                 irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
677                 break;
678         case IRCOMM_TTY_DISCOVERY_INDICATION:
679                 self->daddr = info->daddr;
680                 self->saddr = info->saddr;
681
682                 if (self->iriap) {
683                         IRDA_WARNING("%s(), busy with a previous query\n",
684                                      __FUNCTION__);
685                         return -EBUSY;
686                 }
687
688                 self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
689                                          ircomm_tty_getvalue_confirm);
690
691                 iriap_getvaluebyclass_request(self->iriap,
692                                               self->saddr, self->daddr,
693                                               "IrDA:IrCOMM", "Parameters");
694
695                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
696                 ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
697                 break;
698         case IRCOMM_TTY_CONNECT_INDICATION:
699                 del_timer(&self->watchdog_timer);
700
701                 /* Accept connection */
702                 ircomm_connect_response(self->ircomm, NULL);
703                 ircomm_tty_next_state(self, IRCOMM_TTY_READY);
704                 break;
705         case IRCOMM_TTY_WD_TIMER_EXPIRED:
706                 /* Just stay idle */
707                 break;
708         case IRCOMM_TTY_DETACH_CABLE:
709                 ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
710                 break;
711         default:
712                 IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
713                            ircomm_tty_event[event]);
714                 ret = -EINVAL;
715         }
716         return ret;
717 }
718
719 /*
720  * Function ircomm_tty_state_search (self, event, skb, info)
721  *
722  *    Trying to discover an IrCOMM device
723  *
724  */
725 static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
726                                    IRCOMM_TTY_EVENT event,
727                                    struct sk_buff *skb,
728                                    struct ircomm_tty_info *info)
729 {
730         int ret = 0;
731
732         IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ ,
733                    ircomm_tty_state[self->state], ircomm_tty_event[event]);
734
735         switch (event) {
736         case IRCOMM_TTY_DISCOVERY_INDICATION:
737                 self->daddr = info->daddr;
738                 self->saddr = info->saddr;
739
740                 if (self->iriap) {
741                         IRDA_WARNING("%s(), busy with a previous query\n",
742                                      __FUNCTION__);
743                         return -EBUSY;
744                 }
745
746                 self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
747                                          ircomm_tty_getvalue_confirm);
748
749                 if (self->service_type == IRCOMM_3_WIRE_RAW) {
750                         iriap_getvaluebyclass_request(self->iriap, self->saddr,
751                                                       self->daddr, "IrLPT",
752                                                       "IrDA:IrLMP:LsapSel");
753                         ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
754                 } else {
755                         iriap_getvaluebyclass_request(self->iriap, self->saddr,
756                                                       self->daddr,
757                                                       "IrDA:IrCOMM",
758                                                       "Parameters");
759
760                         ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
761                 }
762                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
763                 break;
764         case IRCOMM_TTY_CONNECT_INDICATION:
765                 del_timer(&self->watchdog_timer);
766                 ircomm_tty_ias_unregister(self);
767
768                 /* Accept connection */
769                 ircomm_connect_response(self->ircomm, NULL);
770                 ircomm_tty_next_state(self, IRCOMM_TTY_READY);
771                 break;
772         case IRCOMM_TTY_WD_TIMER_EXPIRED:
773 #if 1
774                 /* Give up */
775 #else
776                 /* Try to discover any remote devices */
777                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
778                 irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
779 #endif
780                 break;
781         case IRCOMM_TTY_DETACH_CABLE:
782                 ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
783                 break;
784         default:
785                 IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
786                            ircomm_tty_event[event]);
787                 ret = -EINVAL;
788         }
789         return ret;
790 }
791
792 /*
793  * Function ircomm_tty_state_query (self, event, skb, info)
794  *
795  *    Querying the remote LM-IAS for IrCOMM parameters
796  *
797  */
798 static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
799                                              IRCOMM_TTY_EVENT event,
800                                              struct sk_buff *skb,
801                                              struct ircomm_tty_info *info)
802 {
803         int ret = 0;
804
805         IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ ,
806                    ircomm_tty_state[self->state], ircomm_tty_event[event]);
807
808         switch (event) {
809         case IRCOMM_TTY_GOT_PARAMETERS:
810                 if (self->iriap) {
811                         IRDA_WARNING("%s(), busy with a previous query\n",
812                                      __FUNCTION__);
813                         return -EBUSY;
814                 }
815
816                 self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
817                                          ircomm_tty_getvalue_confirm);
818
819                 iriap_getvaluebyclass_request(self->iriap, self->saddr,
820                                               self->daddr, "IrDA:IrCOMM",
821                                               "IrDA:TinyTP:LsapSel");
822
823                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
824                 ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
825                 break;
826         case IRCOMM_TTY_WD_TIMER_EXPIRED:
827                 /* Go back to search mode */
828                 ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
829                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
830                 break;
831         case IRCOMM_TTY_CONNECT_INDICATION:
832                 del_timer(&self->watchdog_timer);
833                 ircomm_tty_ias_unregister(self);
834
835                 /* Accept connection */
836                 ircomm_connect_response(self->ircomm, NULL);
837                 ircomm_tty_next_state(self, IRCOMM_TTY_READY);
838                 break;
839         case IRCOMM_TTY_DETACH_CABLE:
840                 ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
841                 break;
842         default:
843                 IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
844                            ircomm_tty_event[event]);
845                 ret = -EINVAL;
846         }
847         return ret;
848 }
849
850 /*
851  * Function ircomm_tty_state_query_lsap_sel (self, event, skb, info)
852  *
853  *    Query remote LM-IAS for the LSAP selector which we can connect to
854  *
855  */
856 static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
857                                            IRCOMM_TTY_EVENT event,
858                                            struct sk_buff *skb,
859                                            struct ircomm_tty_info *info)
860 {
861         int ret = 0;
862
863         IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ ,
864                    ircomm_tty_state[self->state], ircomm_tty_event[event]);
865
866         switch (event) {
867         case IRCOMM_TTY_GOT_LSAPSEL:
868                 /* Connect to remote device */
869                 ret = ircomm_connect_request(self->ircomm, self->dlsap_sel,
870                                              self->saddr, self->daddr,
871                                              NULL, self->service_type);
872                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
873                 ircomm_tty_next_state(self, IRCOMM_TTY_SETUP);
874                 break;
875         case IRCOMM_TTY_WD_TIMER_EXPIRED:
876                 /* Go back to search mode */
877                 ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
878                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
879                 break;
880         case IRCOMM_TTY_CONNECT_INDICATION:
881                 del_timer(&self->watchdog_timer);
882                 ircomm_tty_ias_unregister(self);
883
884                 /* Accept connection */
885                 ircomm_connect_response(self->ircomm, NULL);
886                 ircomm_tty_next_state(self, IRCOMM_TTY_READY);
887                 break;
888         case IRCOMM_TTY_DETACH_CABLE:
889                 ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
890                 break;
891         default:
892                 IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
893                            ircomm_tty_event[event]);
894                 ret = -EINVAL;
895         }
896         return ret;
897 }
898
899 /*
900  * Function ircomm_tty_state_setup (self, event, skb, info)
901  *
902  *    Trying to connect
903  *
904  */
905 static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
906                                   IRCOMM_TTY_EVENT event,
907                                   struct sk_buff *skb,
908                                   struct ircomm_tty_info *info)
909 {
910         int ret = 0;
911
912         IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ ,
913                    ircomm_tty_state[self->state], ircomm_tty_event[event]);
914
915         switch (event) {
916         case IRCOMM_TTY_CONNECT_CONFIRM:
917                 del_timer(&self->watchdog_timer);
918                 ircomm_tty_ias_unregister(self);
919
920                 /*
921                  * Send initial parameters. This will also send out queued
922                  * parameters waiting for the connection to come up
923                  */
924                 ircomm_tty_send_initial_parameters(self);
925                 ircomm_tty_link_established(self);
926                 ircomm_tty_next_state(self, IRCOMM_TTY_READY);
927                 break;
928         case IRCOMM_TTY_CONNECT_INDICATION:
929                 del_timer(&self->watchdog_timer);
930                 ircomm_tty_ias_unregister(self);
931
932                 /* Accept connection */
933                 ircomm_connect_response(self->ircomm, NULL);
934                 ircomm_tty_next_state(self, IRCOMM_TTY_READY);
935                 break;
936         case IRCOMM_TTY_WD_TIMER_EXPIRED:
937                 /* Go back to search mode */
938                 ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
939                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
940                 break;
941         case IRCOMM_TTY_DETACH_CABLE:
942                 /* ircomm_disconnect_request(self->ircomm, NULL); */
943                 ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
944                 break;
945         default:
946                 IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
947                            ircomm_tty_event[event]);
948                 ret = -EINVAL;
949         }
950         return ret;
951 }
952
953 /*
954  * Function ircomm_tty_state_ready (self, event, skb, info)
955  *
956  *    IrCOMM is now connected
957  *
958  */
959 static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
960                                   IRCOMM_TTY_EVENT event,
961                                   struct sk_buff *skb,
962                                   struct ircomm_tty_info *info)
963 {
964         int ret = 0;
965
966         switch (event) {
967         case IRCOMM_TTY_DATA_REQUEST:
968                 ret = ircomm_data_request(self->ircomm, skb);
969                 break;
970         case IRCOMM_TTY_DETACH_CABLE:
971                 ircomm_disconnect_request(self->ircomm, NULL);
972                 ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
973                 break;
974         case IRCOMM_TTY_DISCONNECT_INDICATION:
975                 ircomm_tty_ias_register(self);
976                 ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
977                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
978
979                 if (self->flags & ASYNC_CHECK_CD) {
980                         /* Drop carrier */
981                         self->settings.dce = IRCOMM_DELTA_CD;
982                         ircomm_tty_check_modem_status(self);
983                 } else {
984                         IRDA_DEBUG(0, "%s(), hanging up!\n", __FUNCTION__ );
985                         if (self->tty)
986                                 tty_hangup(self->tty);
987                 }
988                 break;
989         default:
990                 IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
991                            ircomm_tty_event[event]);
992                 ret = -EINVAL;
993         }
994         return ret;
995 }
996