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