Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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