linux/net/irda/ircomm/ircomm_lmp.c
<<
>>
Prefs
   1/*********************************************************************
   2 *
   3 * Filename:      ircomm_lmp.c
   4 * Version:       1.0
   5 * Description:   Interface between IrCOMM and IrLMP
   6 * Status:        Stable
   7 * Author:        Dag Brattli <dagb@cs.uit.no>
   8 * Created at:    Sun Jun  6 20:48:27 1999
   9 * Modified at:   Sun Dec 12 13:44:17 1999
  10 * Modified by:   Dag Brattli <dagb@cs.uit.no>
  11 * Sources:       Previous IrLPT work by Thomas Davis
  12 *
  13 *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
  14 *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
  15 *
  16 *     This program is free software; you can redistribute it and/or
  17 *     modify it under the terms of the GNU General Public License as
  18 *     published by the Free Software Foundation; either version 2 of
  19 *     the License, or (at your option) any later version.
  20 *
  21 *     This program is distributed in the hope that it will be useful,
  22 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  23 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24 *     GNU General Public License for more details.
  25 *
  26 *     You should have received a copy of the GNU General Public License
  27 *     along with this program; if not, write to the Free Software
  28 *     Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  29 *     MA 02111-1307 USA
  30 *
  31 ********************************************************************/
  32
  33#include <linux/init.h>
  34#include <linux/gfp.h>
  35
  36#include <net/irda/irda.h>
  37#include <net/irda/irlmp.h>
  38#include <net/irda/iriap.h>
  39#include <net/irda/irda_device.h>       /* struct irda_skb_cb */
  40
  41#include <net/irda/ircomm_event.h>
  42#include <net/irda/ircomm_lmp.h>
  43
  44
  45/*
  46 * Function ircomm_lmp_connect_request (self, userdata)
  47 *
  48 *
  49 *
  50 */
  51static int ircomm_lmp_connect_request(struct ircomm_cb *self,
  52                                      struct sk_buff *userdata,
  53                                      struct ircomm_info *info)
  54{
  55        int ret = 0;
  56
  57        IRDA_DEBUG(0, "%s()\n", __func__ );
  58
  59        /* Don't forget to refcount it - should be NULL anyway */
  60        if(userdata)
  61                skb_get(userdata);
  62
  63        ret = irlmp_connect_request(self->lsap, info->dlsap_sel,
  64                                    info->saddr, info->daddr, NULL, userdata);
  65        return ret;
  66}
  67
  68/*
  69 * Function ircomm_lmp_connect_response (self, skb)
  70 *
  71 *
  72 *
  73 */
  74static int ircomm_lmp_connect_response(struct ircomm_cb *self,
  75                                       struct sk_buff *userdata)
  76{
  77        struct sk_buff *tx_skb;
  78
  79        IRDA_DEBUG(0, "%s()\n", __func__ );
  80
  81        /* Any userdata supplied? */
  82        if (userdata == NULL) {
  83                tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
  84                if (!tx_skb)
  85                        return -ENOMEM;
  86
  87                /* Reserve space for MUX and LAP header */
  88                skb_reserve(tx_skb, LMP_MAX_HEADER);
  89        } else {
  90                /*
  91                 *  Check that the client has reserved enough space for
  92                 *  headers
  93                 */
  94                IRDA_ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER,
  95                            return -1;);
  96
  97                /* Don't forget to refcount it - should be NULL anyway */
  98                skb_get(userdata);
  99                tx_skb = userdata;
 100        }
 101
 102        return irlmp_connect_response(self->lsap, tx_skb);
 103}
 104
 105static int ircomm_lmp_disconnect_request(struct ircomm_cb *self,
 106                                         struct sk_buff *userdata,
 107                                         struct ircomm_info *info)
 108{
 109        struct sk_buff *tx_skb;
 110        int ret;
 111
 112        IRDA_DEBUG(0, "%s()\n", __func__ );
 113
 114        if (!userdata) {
 115                tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
 116                if (!tx_skb)
 117                        return -ENOMEM;
 118
 119                /*  Reserve space for MUX and LAP header */
 120                skb_reserve(tx_skb, LMP_MAX_HEADER);
 121                userdata = tx_skb;
 122        } else {
 123                /* Don't forget to refcount it - should be NULL anyway */
 124                skb_get(userdata);
 125        }
 126
 127        ret = irlmp_disconnect_request(self->lsap, userdata);
 128
 129        return ret;
 130}
 131
 132/*
 133 * Function ircomm_lmp_flow_control (skb)
 134 *
 135 *    This function is called when a data frame we have sent to IrLAP has
 136 *    been deallocated. We do this to make sure we don't flood IrLAP with
 137 *    frames, since we are not using the IrTTP flow control mechanism
 138 */
 139static void ircomm_lmp_flow_control(struct sk_buff *skb)
 140{
 141        struct irda_skb_cb *cb;
 142        struct ircomm_cb *self;
 143        int line;
 144
 145        IRDA_ASSERT(skb != NULL, return;);
 146
 147        cb = (struct irda_skb_cb *) skb->cb;
 148
 149        IRDA_DEBUG(2, "%s()\n", __func__ );
 150
 151        line = cb->line;
 152
 153        self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL);
 154        if (!self) {
 155                IRDA_DEBUG(2, "%s(), didn't find myself\n", __func__ );
 156                return;
 157        }
 158
 159        IRDA_ASSERT(self != NULL, return;);
 160        IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
 161
 162        self->pkt_count--;
 163
 164        if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) {
 165                IRDA_DEBUG(2, "%s(), asking TTY to start again!\n", __func__ );
 166                self->flow_status = FLOW_START;
 167                if (self->notify.flow_indication)
 168                        self->notify.flow_indication(self->notify.instance,
 169                                                     self, FLOW_START);
 170        }
 171}
 172
 173/*
 174 * Function ircomm_lmp_data_request (self, userdata)
 175 *
 176 *    Send data frame to peer device
 177 *
 178 */
 179static int ircomm_lmp_data_request(struct ircomm_cb *self,
 180                                   struct sk_buff *skb,
 181                                   int not_used)
 182{
 183        struct irda_skb_cb *cb;
 184        int ret;
 185
 186        IRDA_ASSERT(skb != NULL, return -1;);
 187
 188        cb = (struct irda_skb_cb *) skb->cb;
 189
 190        cb->line = self->line;
 191
 192        IRDA_DEBUG(4, "%s(), sending frame\n", __func__ );
 193
 194        /* Don't forget to refcount it - see ircomm_tty_do_softint() */
 195        skb_get(skb);
 196
 197        skb_orphan(skb);
 198        skb->destructor = ircomm_lmp_flow_control;
 199
 200        if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) {
 201                IRDA_DEBUG(2, "%s(), asking TTY to slow down!\n", __func__ );
 202                self->flow_status = FLOW_STOP;
 203                if (self->notify.flow_indication)
 204                        self->notify.flow_indication(self->notify.instance,
 205                                                     self, FLOW_STOP);
 206        }
 207        ret = irlmp_data_request(self->lsap, skb);
 208        if (ret) {
 209                IRDA_ERROR("%s(), failed\n", __func__);
 210                /* irlmp_data_request already free the packet */
 211        }
 212
 213        return ret;
 214}
 215
 216/*
 217 * Function ircomm_lmp_data_indication (instance, sap, skb)
 218 *
 219 *    Incoming data which we must deliver to the state machine, to check
 220 *    we are still connected.
 221 */
 222static int ircomm_lmp_data_indication(void *instance, void *sap,
 223                                      struct sk_buff *skb)
 224{
 225        struct ircomm_cb *self = (struct ircomm_cb *) instance;
 226
 227        IRDA_DEBUG(4, "%s()\n", __func__ );
 228
 229        IRDA_ASSERT(self != NULL, return -1;);
 230        IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
 231        IRDA_ASSERT(skb != NULL, return -1;);
 232
 233        ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL);
 234
 235        /* Drop reference count - see ircomm_tty_data_indication(). */
 236        dev_kfree_skb(skb);
 237
 238        return 0;
 239}
 240
 241/*
 242 * Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size,
 243 *                                       max_header_size, skb)
 244 *
 245 *    Connection has been confirmed by peer device
 246 *
 247 */
 248static void ircomm_lmp_connect_confirm(void *instance, void *sap,
 249                                       struct qos_info *qos,
 250                                       __u32 max_seg_size,
 251                                       __u8 max_header_size,
 252                                       struct sk_buff *skb)
 253{
 254        struct ircomm_cb *self = (struct ircomm_cb *) instance;
 255        struct ircomm_info info;
 256
 257        IRDA_DEBUG(0, "%s()\n", __func__ );
 258
 259        IRDA_ASSERT(self != NULL, return;);
 260        IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
 261        IRDA_ASSERT(skb != NULL, return;);
 262        IRDA_ASSERT(qos != NULL, return;);
 263
 264        info.max_data_size = max_seg_size;
 265        info.max_header_size = max_header_size;
 266        info.qos = qos;
 267
 268        ircomm_do_event(self, IRCOMM_LMP_CONNECT_CONFIRM, skb, &info);
 269
 270        /* Drop reference count - see ircomm_tty_connect_confirm(). */
 271        dev_kfree_skb(skb);
 272}
 273
 274/*
 275 * Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size,
 276 *                                         max_header_size, skb)
 277 *
 278 *    Peer device wants to make a connection with us
 279 *
 280 */
 281static void ircomm_lmp_connect_indication(void *instance, void *sap,
 282                                          struct qos_info *qos,
 283                                          __u32 max_seg_size,
 284                                          __u8 max_header_size,
 285                                          struct sk_buff *skb)
 286{
 287        struct ircomm_cb *self = (struct ircomm_cb *)instance;
 288        struct ircomm_info info;
 289
 290        IRDA_DEBUG(0, "%s()\n", __func__ );
 291
 292        IRDA_ASSERT(self != NULL, return;);
 293        IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
 294        IRDA_ASSERT(skb != NULL, return;);
 295        IRDA_ASSERT(qos != NULL, return;);
 296
 297        info.max_data_size = max_seg_size;
 298        info.max_header_size = max_header_size;
 299        info.qos = qos;
 300
 301        ircomm_do_event(self, IRCOMM_LMP_CONNECT_INDICATION, skb, &info);
 302
 303        /* Drop reference count - see ircomm_tty_connect_indication(). */
 304        dev_kfree_skb(skb);
 305}
 306
 307/*
 308 * Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb)
 309 *
 310 *    Peer device has closed the connection, or the link went down for some
 311 *    other reason
 312 */
 313static void ircomm_lmp_disconnect_indication(void *instance, void *sap,
 314                                             LM_REASON reason,
 315                                             struct sk_buff *skb)
 316{
 317        struct ircomm_cb *self = (struct ircomm_cb *) instance;
 318        struct ircomm_info info;
 319
 320        IRDA_DEBUG(0, "%s()\n", __func__ );
 321
 322        IRDA_ASSERT(self != NULL, return;);
 323        IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
 324
 325        info.reason = reason;
 326
 327        ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info);
 328
 329        /* Drop reference count - see ircomm_tty_disconnect_indication(). */
 330        if(skb)
 331                dev_kfree_skb(skb);
 332}
 333/*
 334 * Function ircomm_open_lsap (self)
 335 *
 336 *    Open LSAP. This function will only be used when using "raw" services
 337 *
 338 */
 339int ircomm_open_lsap(struct ircomm_cb *self)
 340{
 341        notify_t notify;
 342
 343        IRDA_DEBUG(0, "%s()\n", __func__ );
 344
 345        /* Register callbacks */
 346        irda_notify_init(&notify);
 347        notify.data_indication       = ircomm_lmp_data_indication;
 348        notify.connect_confirm       = ircomm_lmp_connect_confirm;
 349        notify.connect_indication    = ircomm_lmp_connect_indication;
 350        notify.disconnect_indication = ircomm_lmp_disconnect_indication;
 351        notify.instance = self;
 352        strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
 353
 354        self->lsap = irlmp_open_lsap(LSAP_ANY, &notify, 0);
 355        if (!self->lsap) {
 356                IRDA_DEBUG(0,"%sfailed to allocate tsap\n", __func__ );
 357                return -1;
 358        }
 359        self->slsap_sel = self->lsap->slsap_sel;
 360
 361        /*
 362         *  Initialize the call-table for issuing commands
 363         */
 364        self->issue.data_request       = ircomm_lmp_data_request;
 365        self->issue.connect_request    = ircomm_lmp_connect_request;
 366        self->issue.connect_response   = ircomm_lmp_connect_response;
 367        self->issue.disconnect_request = ircomm_lmp_disconnect_request;
 368
 369        return 0;
 370}
 371