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