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