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        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                        IRDA_DEBUG(0, "%s(), hanging up!\n", __func__ );
1001                        tty_port_tty_hangup(&self->port, false);
1002                }
1003                break;
1004        default:
1005                IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
1006                           ircomm_tty_event[event]);
1007                ret = -EINVAL;
1008        }
1009        return ret;
1010}
1011
1012