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