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, see <http://www.gnu.org/licenses/>.
  28 *
  29 ********************************************************************/
  30
  31#include <linux/init.h>
  32#include <linux/gfp.h>
  33
  34#include <net/irda/irda.h>
  35#include <net/irda/irlmp.h>
  36#include <net/irda/iriap.h>
  37#include <net/irda/irda_device.h>       /* struct irda_skb_cb */
  38
  39#include <net/irda/ircomm_event.h>
  40#include <net/irda/ircomm_lmp.h>
  41
  42
  43/*
  44 * Function ircomm_lmp_connect_request (self, userdata)
  45 *
  46 *
  47 *
  48 */
  49static int ircomm_lmp_connect_request(struct ircomm_cb *self,
  50                                      struct sk_buff *userdata,
  51                                      struct ircomm_info *info)
  52{
  53        int ret = 0;
  54
  55        /* Don't forget to refcount it - should be NULL anyway */
  56        if(userdata)
  57                skb_get(userdata);
  58
  59        ret = irlmp_connect_request(self->lsap, info->dlsap_sel,
  60                                    info->saddr, info->daddr, NULL, userdata);
  61        return ret;
  62}
  63
  64/*
  65 * Function ircomm_lmp_connect_response (self, skb)
  66 *
  67 *
  68 *
  69 */
  70static int ircomm_lmp_connect_response(struct ircomm_cb *self,
  71                                       struct sk_buff *userdata)
  72{
  73        struct sk_buff *tx_skb;
  74
  75        /* Any userdata supplied? */
  76        if (userdata == NULL) {
  77                tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
  78                if (!tx_skb)
  79                        return -ENOMEM;
  80
  81                /* Reserve space for MUX and LAP header */
  82                skb_reserve(tx_skb, LMP_MAX_HEADER);
  83        } else {
  84                /*
  85                 *  Check that the client has reserved enough space for
  86                 *  headers
  87                 */
  88                IRDA_ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER,
  89                            return -1;);
  90
  91                /* Don't forget to refcount it - should be NULL anyway */
  92                skb_get(userdata);
  93                tx_skb = userdata;
  94        }
  95
  96        return irlmp_connect_response(self->lsap, tx_skb);
  97}
  98
  99static int ircomm_lmp_disconnect_request(struct ircomm_cb *self,
 100                                         struct sk_buff *userdata,
 101                                         struct ircomm_info *info)
 102{
 103        struct sk_buff *tx_skb;
 104        int ret;
 105
 106        if (!userdata) {
 107                tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
 108                if (!tx_skb)
 109                        return -ENOMEM;
 110
 111                /*  Reserve space for MUX and LAP header */
 112                skb_reserve(tx_skb, LMP_MAX_HEADER);
 113                userdata = tx_skb;
 114        } else {
 115                /* Don't forget to refcount it - should be NULL anyway */
 116                skb_get(userdata);
 117        }
 118
 119        ret = irlmp_disconnect_request(self->lsap, userdata);
 120
 121        return ret;
 122}
 123
 124/*
 125 * Function ircomm_lmp_flow_control (skb)
 126 *
 127 *    This function is called when a data frame we have sent to IrLAP has
 128 *    been deallocated. We do this to make sure we don't flood IrLAP with
 129 *    frames, since we are not using the IrTTP flow control mechanism
 130 */
 131static void ircomm_lmp_flow_control(struct sk_buff *skb)
 132{
 133        struct irda_skb_cb *cb;
 134        struct ircomm_cb *self;
 135        int line;
 136
 137        IRDA_ASSERT(skb != NULL, return;);
 138
 139        cb = (struct irda_skb_cb *) skb->cb;
 140
 141        line = cb->line;
 142
 143        self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL);
 144        if (!self) {
 145                pr_debug("%s(), didn't find myself\n", __func__);
 146                return;
 147        }
 148
 149        IRDA_ASSERT(self != NULL, return;);
 150        IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
 151
 152        self->pkt_count--;
 153
 154        if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) {
 155                pr_debug("%s(), asking TTY to start again!\n", __func__);
 156                self->flow_status = FLOW_START;
 157                if (self->notify.flow_indication)
 158                        self->notify.flow_indication(self->notify.instance,
 159                                                     self, FLOW_START);
 160        }
 161}
 162
 163/*
 164 * Function ircomm_lmp_data_request (self, userdata)
 165 *
 166 *    Send data frame to peer device
 167 *
 168 */
 169static int ircomm_lmp_data_request(struct ircomm_cb *self,
 170                                   struct sk_buff *skb,
 171                                   int not_used)
 172{
 173        struct irda_skb_cb *cb;
 174        int ret;
 175
 176        IRDA_ASSERT(skb != NULL, return -1;);
 177
 178        cb = (struct irda_skb_cb *) skb->cb;
 179
 180        cb->line = self->line;
 181
 182        pr_debug("%s(), sending frame\n", __func__);
 183
 184        /* Don't forget to refcount it - see ircomm_tty_do_softint() */
 185        skb_get(skb);
 186
 187        skb_orphan(skb);
 188        skb->destructor = ircomm_lmp_flow_control;
 189
 190        if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) {
 191                pr_debug("%s(), asking TTY to slow down!\n", __func__);
 192                self->flow_status = FLOW_STOP;
 193                if (self->notify.flow_indication)
 194                        self->notify.flow_indication(self->notify.instance,
 195                                                     self, FLOW_STOP);
 196        }
 197        ret = irlmp_data_request(self->lsap, skb);
 198        if (ret) {
 199                net_err_ratelimited("%s(), failed\n", __func__);
 200                /* irlmp_data_request already free the packet */
 201        }
 202
 203        return ret;
 204}
 205
 206/*
 207 * Function ircomm_lmp_data_indication (instance, sap, skb)
 208 *
 209 *    Incoming data which we must deliver to the state machine, to check
 210 *    we are still connected.
 211 */
 212static int ircomm_lmp_data_indication(void *instance, void *sap,
 213                                      struct sk_buff *skb)
 214{
 215        struct ircomm_cb *self = (struct ircomm_cb *) instance;
 216
 217        IRDA_ASSERT(self != NULL, return -1;);
 218        IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
 219        IRDA_ASSERT(skb != NULL, return -1;);
 220
 221        ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL);
 222
 223        /* Drop reference count - see ircomm_tty_data_indication(). */
 224        dev_kfree_skb(skb);
 225
 226        return 0;
 227}
 228
 229/*
 230 * Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size,
 231 *                                       max_header_size, skb)
 232 *
 233 *    Connection has been confirmed by peer device
 234 *
 235 */
 236static void ircomm_lmp_connect_confirm(void *instance, void *sap,
 237                                       struct qos_info *qos,
 238                                       __u32 max_seg_size,
 239                                       __u8 max_header_size,
 240                                       struct sk_buff *skb)
 241{
 242        struct ircomm_cb *self = (struct ircomm_cb *) instance;
 243        struct ircomm_info info;
 244
 245        IRDA_ASSERT(self != NULL, return;);
 246        IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
 247        IRDA_ASSERT(skb != NULL, return;);
 248        IRDA_ASSERT(qos != NULL, return;);
 249
 250        info.max_data_size = max_seg_size;
 251        info.max_header_size = max_header_size;
 252        info.qos = qos;
 253
 254        ircomm_do_event(self, IRCOMM_LMP_CONNECT_CONFIRM, skb, &info);
 255
 256        /* Drop reference count - see ircomm_tty_connect_confirm(). */
 257        dev_kfree_skb(skb);
 258}
 259
 260/*
 261 * Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size,
 262 *                                         max_header_size, skb)
 263 *
 264 *    Peer device wants to make a connection with us
 265 *
 266 */
 267static void ircomm_lmp_connect_indication(void *instance, void *sap,
 268                                          struct qos_info *qos,
 269                                          __u32 max_seg_size,
 270                                          __u8 max_header_size,
 271                                          struct sk_buff *skb)
 272{
 273        struct ircomm_cb *self = (struct ircomm_cb *)instance;
 274        struct ircomm_info info;
 275
 276        IRDA_ASSERT(self != NULL, return;);
 277        IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
 278        IRDA_ASSERT(skb != NULL, return;);
 279        IRDA_ASSERT(qos != NULL, return;);
 280
 281        info.max_data_size = max_seg_size;
 282        info.max_header_size = max_header_size;
 283        info.qos = qos;
 284
 285        ircomm_do_event(self, IRCOMM_LMP_CONNECT_INDICATION, skb, &info);
 286
 287        /* Drop reference count - see ircomm_tty_connect_indication(). */
 288        dev_kfree_skb(skb);
 289}
 290
 291/*
 292 * Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb)
 293 *
 294 *    Peer device has closed the connection, or the link went down for some
 295 *    other reason
 296 */
 297static void ircomm_lmp_disconnect_indication(void *instance, void *sap,
 298                                             LM_REASON reason,
 299                                             struct sk_buff *skb)
 300{
 301        struct ircomm_cb *self = (struct ircomm_cb *) instance;
 302        struct ircomm_info info;
 303
 304        IRDA_ASSERT(self != NULL, return;);
 305        IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
 306
 307        info.reason = reason;
 308
 309        ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info);
 310
 311        /* Drop reference count - see ircomm_tty_disconnect_indication(). */
 312        if(skb)
 313                dev_kfree_skb(skb);
 314}
 315/*
 316 * Function ircomm_open_lsap (self)
 317 *
 318 *    Open LSAP. This function will only be used when using "raw" services
 319 *
 320 */
 321int ircomm_open_lsap(struct ircomm_cb *self)
 322{
 323        notify_t notify;
 324
 325        /* Register callbacks */
 326        irda_notify_init(&notify);
 327        notify.data_indication       = ircomm_lmp_data_indication;
 328        notify.connect_confirm       = ircomm_lmp_connect_confirm;
 329        notify.connect_indication    = ircomm_lmp_connect_indication;
 330        notify.disconnect_indication = ircomm_lmp_disconnect_indication;
 331        notify.instance = self;
 332        strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
 333
 334        self->lsap = irlmp_open_lsap(LSAP_ANY, &notify, 0);
 335        if (!self->lsap) {
 336                pr_debug("%sfailed to allocate tsap\n", __func__);
 337                return -1;
 338        }
 339        self->slsap_sel = self->lsap->slsap_sel;
 340
 341        /*
 342         *  Initialize the call-table for issuing commands
 343         */
 344        self->issue.data_request       = ircomm_lmp_data_request;
 345        self->issue.connect_request    = ircomm_lmp_connect_request;
 346        self->issue.connect_response   = ircomm_lmp_connect_response;
 347        self->issue.disconnect_request = ircomm_lmp_disconnect_request;
 348
 349        return 0;
 350}
 351