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