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