linux/net/irda/ircomm/ircomm_tty_attach.c
<<
>>
Prefs
   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        IRDA_DEBUG(0, "%s()\n", __func__ );
 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", __func__ );
 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", __func__ );
 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", __func__ );
 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", __func__ ,
 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", __func__ ,
 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", __func__ ,
 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", __func__ );
 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        self = (struct ircomm_tty_cb *) priv;
 386        ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION,
 387                            NULL, &info);
 388}
 389
 390/*
 391 * Function ircomm_tty_disconnect_indication (instance, sap, reason, skb)
 392 *
 393 *    Link disconnected
 394 *
 395 */
 396void ircomm_tty_disconnect_indication(void *instance, void *sap,
 397                                      LM_REASON reason,
 398                                      struct sk_buff *skb)
 399{
 400        struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 401
 402        IRDA_DEBUG(2, "%s()\n", __func__ );
 403
 404        IRDA_ASSERT(self != NULL, return;);
 405        IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 406
 407        if (!self->tty)
 408                return;
 409
 410        /* This will stop control data transfers */
 411        self->flow = FLOW_STOP;
 412
 413        /* Stop data transfers */
 414        self->tty->hw_stopped = 1;
 415
 416        ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL,
 417                            NULL);
 418}
 419
 420/*
 421 * Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv)
 422 *
 423 *    Got result from the IAS query we make
 424 *
 425 */
 426static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
 427                                        struct ias_value *value,
 428                                        void *priv)
 429{
 430        struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv;
 431
 432        IRDA_DEBUG(2, "%s()\n", __func__ );
 433
 434        IRDA_ASSERT(self != NULL, return;);
 435        IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 436
 437        /* We probably don't need to make any more queries */
 438        iriap_close(self->iriap);
 439        self->iriap = NULL;
 440
 441        /* Check if request succeeded */
 442        if (result != IAS_SUCCESS) {
 443                IRDA_DEBUG(4, "%s(), got NULL value!\n", __func__ );
 444                return;
 445        }
 446
 447        switch (value->type) {
 448        case IAS_OCT_SEQ:
 449                IRDA_DEBUG(2, "%s(), got octet sequence\n", __func__ );
 450
 451                irda_param_extract_all(self, value->t.oct_seq, value->len,
 452                                       &ircomm_param_info);
 453
 454                ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL,
 455                                    NULL);
 456                break;
 457        case IAS_INTEGER:
 458                /* Got LSAP selector */
 459                IRDA_DEBUG(2, "%s(), got lsapsel = %d\n", __func__ ,
 460                           value->t.integer);
 461
 462                if (value->t.integer == -1) {
 463                        IRDA_DEBUG(0, "%s(), invalid value!\n", __func__ );
 464                } else
 465                        self->dlsap_sel = value->t.integer;
 466
 467                ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL);
 468                break;
 469        case IAS_MISSING:
 470                IRDA_DEBUG(0, "%s(), got IAS_MISSING\n", __func__ );
 471                break;
 472        default:
 473                IRDA_DEBUG(0, "%s(), got unknown type!\n", __func__ );
 474                break;
 475        }
 476        irias_delete_value(value);
 477}
 478
 479/*
 480 * Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb)
 481 *
 482 *    Connection confirmed
 483 *
 484 */
 485void ircomm_tty_connect_confirm(void *instance, void *sap,
 486                                struct qos_info *qos,
 487                                __u32 max_data_size,
 488                                __u8 max_header_size,
 489                                struct sk_buff *skb)
 490{
 491        struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 492
 493        IRDA_DEBUG(2, "%s()\n", __func__ );
 494
 495        IRDA_ASSERT(self != NULL, return;);
 496        IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 497
 498        self->client = TRUE;
 499        self->max_data_size = max_data_size;
 500        self->max_header_size = max_header_size;
 501        self->flow = FLOW_START;
 502
 503        ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL);
 504
 505        /* No need to kfree_skb - see ircomm_ttp_connect_confirm() */
 506}
 507
 508/*
 509 * Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size,
 510 *                                         skb)
 511 *
 512 *    we are discovered and being requested to connect by remote device !
 513 *
 514 */
 515void ircomm_tty_connect_indication(void *instance, void *sap,
 516                                   struct qos_info *qos,
 517                                   __u32 max_data_size,
 518                                   __u8 max_header_size,
 519                                   struct sk_buff *skb)
 520{
 521        struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 522        int clen;
 523
 524        IRDA_DEBUG(2, "%s()\n", __func__ );
 525
 526        IRDA_ASSERT(self != NULL, return;);
 527        IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 528
 529        self->client = FALSE;
 530        self->max_data_size = max_data_size;
 531        self->max_header_size = max_header_size;
 532        self->flow = FLOW_START;
 533
 534        clen = skb->data[0];
 535        if (clen)
 536                irda_param_extract_all(self, skb->data+1,
 537                                       IRDA_MIN(skb->len, clen),
 538                                       &ircomm_param_info);
 539
 540        ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL);
 541
 542        /* No need to kfree_skb - see ircomm_ttp_connect_indication() */
 543}
 544
 545/*
 546 * Function ircomm_tty_link_established (self)
 547 *
 548 *    Called when the IrCOMM link is established
 549 *
 550 */
 551void ircomm_tty_link_established(struct ircomm_tty_cb *self)
 552{
 553        IRDA_DEBUG(2, "%s()\n", __func__ );
 554
 555        IRDA_ASSERT(self != NULL, return;);
 556        IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 557
 558        if (!self->tty)
 559                return;
 560
 561        del_timer(&self->watchdog_timer);
 562
 563        /*
 564         * IrCOMM link is now up, and if we are not using hardware
 565         * flow-control, then declare the hardware as running. Otherwise we
 566         * will have to wait for the peer device (DCE) to raise the CTS
 567         * line.
 568         */
 569        if ((self->flags & ASYNC_CTS_FLOW) && ((self->settings.dce & IRCOMM_CTS) == 0)) {
 570                IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __func__ );
 571                return;
 572        } else {
 573                IRDA_DEBUG(1, "%s(), starting hardware!\n", __func__ );
 574
 575                self->tty->hw_stopped = 0;
 576
 577                /* Wake up processes blocked on open */
 578                wake_up_interruptible(&self->open_wait);
 579        }
 580
 581        schedule_work(&self->tqueue);
 582}
 583
 584/*
 585 * Function ircomm_tty_start_watchdog_timer (self, timeout)
 586 *
 587 *    Start the watchdog timer. This timer is used to make sure that any
 588 *    connection attempt is successful, and if not, we will retry after
 589 *    the timeout
 590 */
 591static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
 592                                            int timeout)
 593{
 594        IRDA_ASSERT(self != NULL, return;);
 595        IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 596
 597        irda_start_timer(&self->watchdog_timer, timeout, (void *) self,
 598                         ircomm_tty_watchdog_timer_expired);
 599}
 600
 601/*
 602 * Function ircomm_tty_watchdog_timer_expired (data)
 603 *
 604 *    Called when the connect procedure have taken to much time.
 605 *
 606 */
 607static void ircomm_tty_watchdog_timer_expired(void *data)
 608{
 609        struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data;
 610
 611        IRDA_DEBUG(2, "%s()\n", __func__ );
 612
 613        IRDA_ASSERT(self != NULL, return;);
 614        IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 615
 616        ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL);
 617}
 618
 619
 620/*
 621 * Function ircomm_tty_do_event (self, event, skb)
 622 *
 623 *    Process event
 624 *
 625 */
 626int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
 627                        struct sk_buff *skb, struct ircomm_tty_info *info)
 628{
 629        IRDA_ASSERT(self != NULL, return -1;);
 630        IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
 631
 632        IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
 633                   ircomm_tty_state[self->state], ircomm_tty_event[event]);
 634
 635        return (*state[self->state])(self, event, skb, info);
 636}
 637
 638/*
 639 * Function ircomm_tty_next_state (self, state)
 640 *
 641 *    Switch state
 642 *
 643 */
 644static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state)
 645{
 646        /*
 647        IRDA_ASSERT(self != NULL, return;);
 648        IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 649
 650        IRDA_DEBUG(2, "%s: next state=%s, service type=%d\n", __func__ ,
 651                   ircomm_tty_state[self->state], self->service_type);
 652        */
 653        self->state = state;
 654}
 655
 656/*
 657 * Function ircomm_tty_state_idle (self, event, skb, info)
 658 *
 659 *    Just hanging around
 660 *
 661 */
 662static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
 663                                 IRCOMM_TTY_EVENT event,
 664                                 struct sk_buff *skb,
 665                                 struct ircomm_tty_info *info)
 666{
 667        int ret = 0;
 668
 669        IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
 670                   ircomm_tty_state[self->state], ircomm_tty_event[event]);
 671        switch (event) {
 672        case IRCOMM_TTY_ATTACH_CABLE:
 673                /* Try to discover any remote devices */
 674                ircomm_tty_start_watchdog_timer(self, 3*HZ);
 675                ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
 676
 677                irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
 678                break;
 679        case IRCOMM_TTY_DISCOVERY_INDICATION:
 680                self->daddr = info->daddr;
 681                self->saddr = info->saddr;
 682
 683                if (self->iriap) {
 684                        IRDA_WARNING("%s(), busy with a previous query\n",
 685                                     __func__);
 686                        return -EBUSY;
 687                }
 688
 689                self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
 690                                         ircomm_tty_getvalue_confirm);
 691
 692                iriap_getvaluebyclass_request(self->iriap,
 693                                              self->saddr, self->daddr,
 694                                              "IrDA:IrCOMM", "Parameters");
 695
 696                ircomm_tty_start_watchdog_timer(self, 3*HZ);
 697                ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
 698                break;
 699        case IRCOMM_TTY_CONNECT_INDICATION:
 700                del_timer(&self->watchdog_timer);
 701
 702                /* Accept connection */
 703                ircomm_connect_response(self->ircomm, NULL);
 704                ircomm_tty_next_state(self, IRCOMM_TTY_READY);
 705                break;
 706        case IRCOMM_TTY_WD_TIMER_EXPIRED:
 707                /* Just stay idle */
 708                break;
 709        case IRCOMM_TTY_DETACH_CABLE:
 710                ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
 711                break;
 712        default:
 713                IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
 714                           ircomm_tty_event[event]);
 715                ret = -EINVAL;
 716        }
 717        return ret;
 718}
 719
 720/*
 721 * Function ircomm_tty_state_search (self, event, skb, info)
 722 *
 723 *    Trying to discover an IrCOMM device
 724 *
 725 */
 726static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
 727                                   IRCOMM_TTY_EVENT event,
 728                                   struct sk_buff *skb,
 729                                   struct ircomm_tty_info *info)
 730{
 731        int ret = 0;
 732
 733        IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
 734                   ircomm_tty_state[self->state], ircomm_tty_event[event]);
 735
 736        switch (event) {
 737        case IRCOMM_TTY_DISCOVERY_INDICATION:
 738                self->daddr = info->daddr;
 739                self->saddr = info->saddr;
 740
 741                if (self->iriap) {
 742                        IRDA_WARNING("%s(), busy with a previous query\n",
 743                                     __func__);
 744                        return -EBUSY;
 745                }
 746
 747                self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
 748                                         ircomm_tty_getvalue_confirm);
 749
 750                if (self->service_type == IRCOMM_3_WIRE_RAW) {
 751                        iriap_getvaluebyclass_request(self->iriap, self->saddr,
 752                                                      self->daddr, "IrLPT",
 753                                                      "IrDA:IrLMP:LsapSel");
 754                        ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
 755                } else {
 756                        iriap_getvaluebyclass_request(self->iriap, self->saddr,
 757                                                      self->daddr,
 758                                                      "IrDA:IrCOMM",
 759                                                      "Parameters");
 760
 761                        ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
 762                }
 763                ircomm_tty_start_watchdog_timer(self, 3*HZ);
 764                break;
 765        case IRCOMM_TTY_CONNECT_INDICATION:
 766                del_timer(&self->watchdog_timer);
 767                ircomm_tty_ias_unregister(self);
 768
 769                /* Accept connection */
 770                ircomm_connect_response(self->ircomm, NULL);
 771                ircomm_tty_next_state(self, IRCOMM_TTY_READY);
 772                break;
 773        case IRCOMM_TTY_WD_TIMER_EXPIRED:
 774#if 1
 775                /* Give up */
 776#else
 777                /* Try to discover any remote devices */
 778                ircomm_tty_start_watchdog_timer(self, 3*HZ);
 779                irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
 780#endif
 781                break;
 782        case IRCOMM_TTY_DETACH_CABLE:
 783                ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
 784                break;
 785        default:
 786                IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
 787                           ircomm_tty_event[event]);
 788                ret = -EINVAL;
 789        }
 790        return ret;
 791}
 792
 793/*
 794 * Function ircomm_tty_state_query (self, event, skb, info)
 795 *
 796 *    Querying the remote LM-IAS for IrCOMM parameters
 797 *
 798 */
 799static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
 800                                             IRCOMM_TTY_EVENT event,
 801                                             struct sk_buff *skb,
 802                                             struct ircomm_tty_info *info)
 803{
 804        int ret = 0;
 805
 806        IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
 807                   ircomm_tty_state[self->state], ircomm_tty_event[event]);
 808
 809        switch (event) {
 810        case IRCOMM_TTY_GOT_PARAMETERS:
 811                if (self->iriap) {
 812                        IRDA_WARNING("%s(), busy with a previous query\n",
 813                                     __func__);
 814                        return -EBUSY;
 815                }
 816
 817                self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
 818                                         ircomm_tty_getvalue_confirm);
 819
 820                iriap_getvaluebyclass_request(self->iriap, self->saddr,
 821                                              self->daddr, "IrDA:IrCOMM",
 822                                              "IrDA:TinyTP:LsapSel");
 823
 824                ircomm_tty_start_watchdog_timer(self, 3*HZ);
 825                ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
 826                break;
 827        case IRCOMM_TTY_WD_TIMER_EXPIRED:
 828                /* Go back to search mode */
 829                ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
 830                ircomm_tty_start_watchdog_timer(self, 3*HZ);
 831                break;
 832        case IRCOMM_TTY_CONNECT_INDICATION:
 833                del_timer(&self->watchdog_timer);
 834                ircomm_tty_ias_unregister(self);
 835
 836                /* Accept connection */
 837                ircomm_connect_response(self->ircomm, NULL);
 838                ircomm_tty_next_state(self, IRCOMM_TTY_READY);
 839                break;
 840        case IRCOMM_TTY_DETACH_CABLE:
 841                ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
 842                break;
 843        default:
 844                IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
 845                           ircomm_tty_event[event]);
 846                ret = -EINVAL;
 847        }
 848        return ret;
 849}
 850
 851/*
 852 * Function ircomm_tty_state_query_lsap_sel (self, event, skb, info)
 853 *
 854 *    Query remote LM-IAS for the LSAP selector which we can connect to
 855 *
 856 */
 857static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
 858                                           IRCOMM_TTY_EVENT event,
 859                                           struct sk_buff *skb,
 860                                           struct ircomm_tty_info *info)
 861{
 862        int ret = 0;
 863
 864        IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
 865                   ircomm_tty_state[self->state], ircomm_tty_event[event]);
 866
 867        switch (event) {
 868        case IRCOMM_TTY_GOT_LSAPSEL:
 869                /* Connect to remote device */
 870                ret = ircomm_connect_request(self->ircomm, self->dlsap_sel,
 871                                             self->saddr, self->daddr,
 872                                             NULL, self->service_type);
 873                ircomm_tty_start_watchdog_timer(self, 3*HZ);
 874                ircomm_tty_next_state(self, IRCOMM_TTY_SETUP);
 875                break;
 876        case IRCOMM_TTY_WD_TIMER_EXPIRED:
 877                /* Go back to search mode */
 878                ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
 879                ircomm_tty_start_watchdog_timer(self, 3*HZ);
 880                break;
 881        case IRCOMM_TTY_CONNECT_INDICATION:
 882                del_timer(&self->watchdog_timer);
 883                ircomm_tty_ias_unregister(self);
 884
 885                /* Accept connection */
 886                ircomm_connect_response(self->ircomm, NULL);
 887                ircomm_tty_next_state(self, IRCOMM_TTY_READY);
 888                break;
 889        case IRCOMM_TTY_DETACH_CABLE:
 890                ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
 891                break;
 892        default:
 893                IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
 894                           ircomm_tty_event[event]);
 895                ret = -EINVAL;
 896        }
 897        return ret;
 898}
 899
 900/*
 901 * Function ircomm_tty_state_setup (self, event, skb, info)
 902 *
 903 *    Trying to connect
 904 *
 905 */
 906static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
 907                                  IRCOMM_TTY_EVENT event,
 908                                  struct sk_buff *skb,
 909                                  struct ircomm_tty_info *info)
 910{
 911        int ret = 0;
 912
 913        IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
 914                   ircomm_tty_state[self->state], ircomm_tty_event[event]);
 915
 916        switch (event) {
 917        case IRCOMM_TTY_CONNECT_CONFIRM:
 918                del_timer(&self->watchdog_timer);
 919                ircomm_tty_ias_unregister(self);
 920
 921                /*
 922                 * Send initial parameters. This will also send out queued
 923                 * parameters waiting for the connection to come up
 924                 */
 925                ircomm_tty_send_initial_parameters(self);
 926                ircomm_tty_link_established(self);
 927                ircomm_tty_next_state(self, IRCOMM_TTY_READY);
 928                break;
 929        case IRCOMM_TTY_CONNECT_INDICATION:
 930                del_timer(&self->watchdog_timer);
 931                ircomm_tty_ias_unregister(self);
 932
 933                /* Accept connection */
 934                ircomm_connect_response(self->ircomm, NULL);
 935                ircomm_tty_next_state(self, IRCOMM_TTY_READY);
 936                break;
 937        case IRCOMM_TTY_WD_TIMER_EXPIRED:
 938                /* Go back to search mode */
 939                ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
 940                ircomm_tty_start_watchdog_timer(self, 3*HZ);
 941                break;
 942        case IRCOMM_TTY_DETACH_CABLE:
 943                /* ircomm_disconnect_request(self->ircomm, NULL); */
 944                ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
 945                break;
 946        default:
 947                IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
 948                           ircomm_tty_event[event]);
 949                ret = -EINVAL;
 950        }
 951        return ret;
 952}
 953
 954/*
 955 * Function ircomm_tty_state_ready (self, event, skb, info)
 956 *
 957 *    IrCOMM is now connected
 958 *
 959 */
 960static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
 961                                  IRCOMM_TTY_EVENT event,
 962                                  struct sk_buff *skb,
 963                                  struct ircomm_tty_info *info)
 964{
 965        int ret = 0;
 966
 967        switch (event) {
 968        case IRCOMM_TTY_DATA_REQUEST:
 969                ret = ircomm_data_request(self->ircomm, skb);
 970                break;
 971        case IRCOMM_TTY_DETACH_CABLE:
 972                ircomm_disconnect_request(self->ircomm, NULL);
 973                ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
 974                break;
 975        case IRCOMM_TTY_DISCONNECT_INDICATION:
 976                ircomm_tty_ias_register(self);
 977                ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
 978                ircomm_tty_start_watchdog_timer(self, 3*HZ);
 979
 980                if (self->flags & ASYNC_CHECK_CD) {
 981                        /* Drop carrier */
 982                        self->settings.dce = IRCOMM_DELTA_CD;
 983                        ircomm_tty_check_modem_status(self);
 984                } else {
 985                        IRDA_DEBUG(0, "%s(), hanging up!\n", __func__ );
 986                        if (self->tty)
 987                                tty_hangup(self->tty);
 988                }
 989                break;
 990        default:
 991                IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
 992                           ircomm_tty_event[event]);
 993                ret = -EINVAL;
 994        }
 995        return ret;
 996}
 997
 998