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