Merge branch 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6
[linux-2.6] / 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         /* FIXME. We have a locking problem on the hashbin here.
385          * We probably need to use hashbin_find_next(), but we first
386          * need to ensure that "line" is unique. - Jean II */
387         self = (struct ircomm_tty_cb *) hashbin_get_first(ircomm_tty);
388         while (self != NULL) {
389                 IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
390
391                 ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION,
392                                     NULL, &info);
393
394                 self = (struct ircomm_tty_cb *) hashbin_get_next(ircomm_tty);
395         }
396 }
397
398 /*
399  * Function ircomm_tty_disconnect_indication (instance, sap, reason, skb)
400  *
401  *    Link disconnected
402  *
403  */
404 void ircomm_tty_disconnect_indication(void *instance, void *sap,
405                                       LM_REASON reason,
406                                       struct sk_buff *skb)
407 {
408         struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
409
410         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
411
412         IRDA_ASSERT(self != NULL, return;);
413         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
414
415         if (!self->tty)
416                 return;
417
418         /* This will stop control data transfers */
419         self->flow = FLOW_STOP;
420
421         /* Stop data transfers */
422         self->tty->hw_stopped = 1;
423
424         ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL,
425                             NULL);
426 }
427
428 /*
429  * Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv)
430  *
431  *    Got result from the IAS query we make
432  *
433  */
434 static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
435                                         struct ias_value *value,
436                                         void *priv)
437 {
438         struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv;
439
440         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
441
442         IRDA_ASSERT(self != NULL, return;);
443         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
444
445         /* We probably don't need to make any more queries */
446         iriap_close(self->iriap);
447         self->iriap = NULL;
448
449         /* Check if request succeeded */
450         if (result != IAS_SUCCESS) {
451                 IRDA_DEBUG(4, "%s(), got NULL value!\n", __FUNCTION__ );
452                 return;
453         }
454
455         switch (value->type) {
456         case IAS_OCT_SEQ:
457                 IRDA_DEBUG(2, "%s(), got octet sequence\n", __FUNCTION__ );
458
459                 irda_param_extract_all(self, value->t.oct_seq, value->len,
460                                        &ircomm_param_info);
461
462                 ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL,
463                                     NULL);
464                 break;
465         case IAS_INTEGER:
466                 /* Got LSAP selector */
467                 IRDA_DEBUG(2, "%s(), got lsapsel = %d\n", __FUNCTION__ ,
468                            value->t.integer);
469
470                 if (value->t.integer == -1) {
471                         IRDA_DEBUG(0, "%s(), invalid value!\n", __FUNCTION__ );
472                 } else
473                         self->dlsap_sel = value->t.integer;
474
475                 ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL);
476                 break;
477         case IAS_MISSING:
478                 IRDA_DEBUG(0, "%s(), got IAS_MISSING\n", __FUNCTION__ );
479                 break;
480         default:
481                 IRDA_DEBUG(0, "%s(), got unknown type!\n", __FUNCTION__ );
482                 break;
483         }
484         irias_delete_value(value);
485 }
486
487 /*
488  * Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb)
489  *
490  *    Connection confirmed
491  *
492  */
493 void ircomm_tty_connect_confirm(void *instance, void *sap,
494                                 struct qos_info *qos,
495                                 __u32 max_data_size,
496                                 __u8 max_header_size,
497                                 struct sk_buff *skb)
498 {
499         struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
500
501         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
502
503         IRDA_ASSERT(self != NULL, return;);
504         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
505
506         self->client = TRUE;
507         self->max_data_size = max_data_size;
508         self->max_header_size = max_header_size;
509         self->flow = FLOW_START;
510
511         ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL);
512
513         /* No need to kfree_skb - see ircomm_ttp_connect_confirm() */
514 }
515
516 /*
517  * Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size,
518  *                                         skb)
519  *
520  *    we are discovered and being requested to connect by remote device !
521  *
522  */
523 void ircomm_tty_connect_indication(void *instance, void *sap,
524                                    struct qos_info *qos,
525                                    __u32 max_data_size,
526                                    __u8 max_header_size,
527                                    struct sk_buff *skb)
528 {
529         struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
530         int clen;
531
532         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
533
534         IRDA_ASSERT(self != NULL, return;);
535         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
536
537         self->client = FALSE;
538         self->max_data_size = max_data_size;
539         self->max_header_size = max_header_size;
540         self->flow = FLOW_START;
541
542         clen = skb->data[0];
543         if (clen)
544                 irda_param_extract_all(self, skb->data+1,
545                                        IRDA_MIN(skb->len, clen),
546                                        &ircomm_param_info);
547
548         ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL);
549
550         /* No need to kfree_skb - see ircomm_ttp_connect_indication() */
551 }
552
553 /*
554  * Function ircomm_tty_link_established (self)
555  *
556  *    Called when the IrCOMM link is established
557  *
558  */
559 void ircomm_tty_link_established(struct ircomm_tty_cb *self)
560 {
561         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
562
563         IRDA_ASSERT(self != NULL, return;);
564         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
565
566         if (!self->tty)
567                 return;
568
569         del_timer(&self->watchdog_timer);
570
571         /*
572          * IrCOMM link is now up, and if we are not using hardware
573          * flow-control, then declare the hardware as running. Otherwise we
574          * will have to wait for the peer device (DCE) to raise the CTS
575          * line.
576          */
577         if ((self->flags & ASYNC_CTS_FLOW) && ((self->settings.dce & IRCOMM_CTS) == 0)) {
578                 IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __FUNCTION__ );
579                 return;
580         } else {
581                 IRDA_DEBUG(1, "%s(), starting hardware!\n", __FUNCTION__ );
582
583                 self->tty->hw_stopped = 0;
584
585                 /* Wake up processes blocked on open */
586                 wake_up_interruptible(&self->open_wait);
587         }
588
589         schedule_work(&self->tqueue);
590 }
591
592 /*
593  * Function ircomm_tty_start_watchdog_timer (self, timeout)
594  *
595  *    Start the watchdog timer. This timer is used to make sure that any
596  *    connection attempt is successful, and if not, we will retry after
597  *    the timeout
598  */
599 static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
600                                             int timeout)
601 {
602         IRDA_ASSERT(self != NULL, return;);
603         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
604
605         irda_start_timer(&self->watchdog_timer, timeout, (void *) self,
606                          ircomm_tty_watchdog_timer_expired);
607 }
608
609 /*
610  * Function ircomm_tty_watchdog_timer_expired (data)
611  *
612  *    Called when the connect procedure have taken to much time.
613  *
614  */
615 static void ircomm_tty_watchdog_timer_expired(void *data)
616 {
617         struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data;
618
619         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
620
621         IRDA_ASSERT(self != NULL, return;);
622         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
623
624         ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL);
625 }
626
627
628 /*
629  * Function ircomm_tty_do_event (self, event, skb)
630  *
631  *    Process event
632  *
633  */
634 int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
635                         struct sk_buff *skb, struct ircomm_tty_info *info)
636 {
637         IRDA_ASSERT(self != NULL, return -1;);
638         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
639
640         IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ ,
641                    ircomm_tty_state[self->state], ircomm_tty_event[event]);
642
643         return (*state[self->state])(self, event, skb, info);
644 }
645
646 /*
647  * Function ircomm_tty_next_state (self, state)
648  *
649  *    Switch state
650  *
651  */
652 static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state)
653 {
654         /*
655         IRDA_ASSERT(self != NULL, return;);
656         IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
657
658         IRDA_DEBUG(2, "%s: next state=%s, service type=%d\n", __FUNCTION__ ,
659                    ircomm_tty_state[self->state], self->service_type);
660         */
661         self->state = state;
662 }
663
664 /*
665  * Function ircomm_tty_state_idle (self, event, skb, info)
666  *
667  *    Just hanging around
668  *
669  */
670 static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
671                                  IRCOMM_TTY_EVENT event,
672                                  struct sk_buff *skb,
673                                  struct ircomm_tty_info *info)
674 {
675         int ret = 0;
676
677         IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ ,
678                    ircomm_tty_state[self->state], ircomm_tty_event[event]);
679         switch (event) {
680         case IRCOMM_TTY_ATTACH_CABLE:
681                 /* Try to discover any remote devices */
682                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
683                 ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
684
685                 irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
686                 break;
687         case IRCOMM_TTY_DISCOVERY_INDICATION:
688                 self->daddr = info->daddr;
689                 self->saddr = info->saddr;
690
691                 if (self->iriap) {
692                         IRDA_WARNING("%s(), busy with a previous query\n",
693                                      __FUNCTION__);
694                         return -EBUSY;
695                 }
696
697                 self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
698                                          ircomm_tty_getvalue_confirm);
699
700                 iriap_getvaluebyclass_request(self->iriap,
701                                               self->saddr, self->daddr,
702                                               "IrDA:IrCOMM", "Parameters");
703
704                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
705                 ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
706                 break;
707         case IRCOMM_TTY_CONNECT_INDICATION:
708                 del_timer(&self->watchdog_timer);
709
710                 /* Accept connection */
711                 ircomm_connect_response(self->ircomm, NULL);
712                 ircomm_tty_next_state(self, IRCOMM_TTY_READY);
713                 break;
714         case IRCOMM_TTY_WD_TIMER_EXPIRED:
715                 /* Just stay idle */
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_search (self, event, skb, info)
730  *
731  *    Trying to discover an IrCOMM device
732  *
733  */
734 static int ircomm_tty_state_search(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_DISCOVERY_INDICATION:
746                 self->daddr = info->daddr;
747                 self->saddr = info->saddr;
748
749                 if (self->iriap) {
750                         IRDA_WARNING("%s(), busy with a previous query\n",
751                                      __FUNCTION__);
752                         return -EBUSY;
753                 }
754
755                 self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
756                                          ircomm_tty_getvalue_confirm);
757
758                 if (self->service_type == IRCOMM_3_WIRE_RAW) {
759                         iriap_getvaluebyclass_request(self->iriap, self->saddr,
760                                                       self->daddr, "IrLPT",
761                                                       "IrDA:IrLMP:LsapSel");
762                         ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
763                 } else {
764                         iriap_getvaluebyclass_request(self->iriap, self->saddr,
765                                                       self->daddr,
766                                                       "IrDA:IrCOMM",
767                                                       "Parameters");
768
769                         ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
770                 }
771                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
772                 break;
773         case IRCOMM_TTY_CONNECT_INDICATION:
774                 del_timer(&self->watchdog_timer);
775                 ircomm_tty_ias_unregister(self);
776
777                 /* Accept connection */
778                 ircomm_connect_response(self->ircomm, NULL);
779                 ircomm_tty_next_state(self, IRCOMM_TTY_READY);
780                 break;
781         case IRCOMM_TTY_WD_TIMER_EXPIRED:
782 #if 1
783                 /* Give up */
784 #else
785                 /* Try to discover any remote devices */
786                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
787                 irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
788 #endif
789                 break;
790         case IRCOMM_TTY_DETACH_CABLE:
791                 ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
792                 break;
793         default:
794                 IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
795                            ircomm_tty_event[event]);
796                 ret = -EINVAL;
797         }
798         return ret;
799 }
800
801 /*
802  * Function ircomm_tty_state_query (self, event, skb, info)
803  *
804  *    Querying the remote LM-IAS for IrCOMM parameters
805  *
806  */
807 static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
808                                              IRCOMM_TTY_EVENT event,
809                                              struct sk_buff *skb,
810                                              struct ircomm_tty_info *info)
811 {
812         int ret = 0;
813
814         IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ ,
815                    ircomm_tty_state[self->state], ircomm_tty_event[event]);
816
817         switch (event) {
818         case IRCOMM_TTY_GOT_PARAMETERS:
819                 if (self->iriap) {
820                         IRDA_WARNING("%s(), busy with a previous query\n",
821                                      __FUNCTION__);
822                         return -EBUSY;
823                 }
824
825                 self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
826                                          ircomm_tty_getvalue_confirm);
827
828                 iriap_getvaluebyclass_request(self->iriap, self->saddr,
829                                               self->daddr, "IrDA:IrCOMM",
830                                               "IrDA:TinyTP:LsapSel");
831
832                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
833                 ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
834                 break;
835         case IRCOMM_TTY_WD_TIMER_EXPIRED:
836                 /* Go back to search mode */
837                 ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
838                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
839                 break;
840         case IRCOMM_TTY_CONNECT_INDICATION:
841                 del_timer(&self->watchdog_timer);
842                 ircomm_tty_ias_unregister(self);
843
844                 /* Accept connection */
845                 ircomm_connect_response(self->ircomm, NULL);
846                 ircomm_tty_next_state(self, IRCOMM_TTY_READY);
847                 break;
848         case IRCOMM_TTY_DETACH_CABLE:
849                 ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
850                 break;
851         default:
852                 IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
853                            ircomm_tty_event[event]);
854                 ret = -EINVAL;
855         }
856         return ret;
857 }
858
859 /*
860  * Function ircomm_tty_state_query_lsap_sel (self, event, skb, info)
861  *
862  *    Query remote LM-IAS for the LSAP selector which we can connect to
863  *
864  */
865 static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
866                                            IRCOMM_TTY_EVENT event,
867                                            struct sk_buff *skb,
868                                            struct ircomm_tty_info *info)
869 {
870         int ret = 0;
871
872         IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ ,
873                    ircomm_tty_state[self->state], ircomm_tty_event[event]);
874
875         switch (event) {
876         case IRCOMM_TTY_GOT_LSAPSEL:
877                 /* Connect to remote device */
878                 ret = ircomm_connect_request(self->ircomm, self->dlsap_sel,
879                                              self->saddr, self->daddr,
880                                              NULL, self->service_type);
881                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
882                 ircomm_tty_next_state(self, IRCOMM_TTY_SETUP);
883                 break;
884         case IRCOMM_TTY_WD_TIMER_EXPIRED:
885                 /* Go back to search mode */
886                 ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
887                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
888                 break;
889         case IRCOMM_TTY_CONNECT_INDICATION:
890                 del_timer(&self->watchdog_timer);
891                 ircomm_tty_ias_unregister(self);
892
893                 /* Accept connection */
894                 ircomm_connect_response(self->ircomm, NULL);
895                 ircomm_tty_next_state(self, IRCOMM_TTY_READY);
896                 break;
897         case IRCOMM_TTY_DETACH_CABLE:
898                 ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
899                 break;
900         default:
901                 IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
902                            ircomm_tty_event[event]);
903                 ret = -EINVAL;
904         }
905         return ret;
906 }
907
908 /*
909  * Function ircomm_tty_state_setup (self, event, skb, info)
910  *
911  *    Trying to connect
912  *
913  */
914 static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
915                                   IRCOMM_TTY_EVENT event,
916                                   struct sk_buff *skb,
917                                   struct ircomm_tty_info *info)
918 {
919         int ret = 0;
920
921         IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ ,
922                    ircomm_tty_state[self->state], ircomm_tty_event[event]);
923
924         switch (event) {
925         case IRCOMM_TTY_CONNECT_CONFIRM:
926                 del_timer(&self->watchdog_timer);
927                 ircomm_tty_ias_unregister(self);
928
929                 /*
930                  * Send initial parameters. This will also send out queued
931                  * parameters waiting for the connection to come up
932                  */
933                 ircomm_tty_send_initial_parameters(self);
934                 ircomm_tty_link_established(self);
935                 ircomm_tty_next_state(self, IRCOMM_TTY_READY);
936                 break;
937         case IRCOMM_TTY_CONNECT_INDICATION:
938                 del_timer(&self->watchdog_timer);
939                 ircomm_tty_ias_unregister(self);
940
941                 /* Accept connection */
942                 ircomm_connect_response(self->ircomm, NULL);
943                 ircomm_tty_next_state(self, IRCOMM_TTY_READY);
944                 break;
945         case IRCOMM_TTY_WD_TIMER_EXPIRED:
946                 /* Go back to search mode */
947                 ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
948                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
949                 break;
950         case IRCOMM_TTY_DETACH_CABLE:
951                 /* ircomm_disconnect_request(self->ircomm, NULL); */
952                 ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
953                 break;
954         default:
955                 IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
956                            ircomm_tty_event[event]);
957                 ret = -EINVAL;
958         }
959         return ret;
960 }
961
962 /*
963  * Function ircomm_tty_state_ready (self, event, skb, info)
964  *
965  *    IrCOMM is now connected
966  *
967  */
968 static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
969                                   IRCOMM_TTY_EVENT event,
970                                   struct sk_buff *skb,
971                                   struct ircomm_tty_info *info)
972 {
973         int ret = 0;
974
975         switch (event) {
976         case IRCOMM_TTY_DATA_REQUEST:
977                 ret = ircomm_data_request(self->ircomm, skb);
978                 break;
979         case IRCOMM_TTY_DETACH_CABLE:
980                 ircomm_disconnect_request(self->ircomm, NULL);
981                 ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
982                 break;
983         case IRCOMM_TTY_DISCONNECT_INDICATION:
984                 ircomm_tty_ias_register(self);
985                 ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
986                 ircomm_tty_start_watchdog_timer(self, 3*HZ);
987
988                 if (self->flags & ASYNC_CHECK_CD) {
989                         /* Drop carrier */
990                         self->settings.dce = IRCOMM_DELTA_CD;
991                         ircomm_tty_check_modem_status(self);
992                 } else {
993                         IRDA_DEBUG(0, "%s(), hanging up!\n", __FUNCTION__ );
994                         if (self->tty)
995                                 tty_hangup(self->tty);
996                 }
997                 break;
998         default:
999                 IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
1000                            ircomm_tty_event[event]);
1001                 ret = -EINVAL;
1002         }
1003         return ret;
1004 }
1005