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