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