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