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