linux/net/bluetooth/cmtp/core.c
<<
>>
Prefs
   1/*
   2   CMTP implementation for Linux Bluetooth stack (BlueZ).
   3   Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
   4
   5   This program is free software; you can redistribute it and/or modify
   6   it under the terms of the GNU General Public License version 2 as
   7   published by the Free Software Foundation;
   8
   9   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  10   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  11   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
  12   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
  13   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
  14   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17
  18   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
  19   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
  20   SOFTWARE IS DISCLAIMED.
  21*/
  22
  23#include <linux/module.h>
  24
  25#include <linux/types.h>
  26#include <linux/errno.h>
  27#include <linux/kernel.h>
  28#include <linux/sched.h>
  29#include <linux/slab.h>
  30#include <linux/poll.h>
  31#include <linux/fcntl.h>
  32#include <linux/freezer.h>
  33#include <linux/skbuff.h>
  34#include <linux/socket.h>
  35#include <linux/ioctl.h>
  36#include <linux/file.h>
  37#include <linux/init.h>
  38#include <linux/kthread.h>
  39#include <net/sock.h>
  40
  41#include <linux/isdn/capilli.h>
  42
  43#include <net/bluetooth/bluetooth.h>
  44#include <net/bluetooth/l2cap.h>
  45
  46#include "cmtp.h"
  47
  48#define VERSION "1.0"
  49
  50static DECLARE_RWSEM(cmtp_session_sem);
  51static LIST_HEAD(cmtp_session_list);
  52
  53static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
  54{
  55        struct cmtp_session *session;
  56
  57        BT_DBG("");
  58
  59        list_for_each_entry(session, &cmtp_session_list, list)
  60                if (!bacmp(bdaddr, &session->bdaddr))
  61                        return session;
  62
  63        return NULL;
  64}
  65
  66static void __cmtp_link_session(struct cmtp_session *session)
  67{
  68        list_add(&session->list, &cmtp_session_list);
  69}
  70
  71static void __cmtp_unlink_session(struct cmtp_session *session)
  72{
  73        list_del(&session->list);
  74}
  75
  76static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
  77{
  78        memset(ci, 0, sizeof(*ci));
  79        bacpy(&ci->bdaddr, &session->bdaddr);
  80
  81        ci->flags = session->flags;
  82        ci->state = session->state;
  83
  84        ci->num = session->num;
  85}
  86
  87
  88static inline int cmtp_alloc_block_id(struct cmtp_session *session)
  89{
  90        int i, id = -1;
  91
  92        for (i = 0; i < 16; i++)
  93                if (!test_and_set_bit(i, &session->blockids)) {
  94                        id = i;
  95                        break;
  96                }
  97
  98        return id;
  99}
 100
 101static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
 102{
 103        clear_bit(id, &session->blockids);
 104}
 105
 106static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
 107{
 108        struct sk_buff *skb = session->reassembly[id], *nskb;
 109        int size;
 110
 111        BT_DBG("session %p buf %p count %d", session, buf, count);
 112
 113        size = (skb) ? skb->len + count : count;
 114
 115        nskb = alloc_skb(size, GFP_ATOMIC);
 116        if (!nskb) {
 117                BT_ERR("Can't allocate memory for CAPI message");
 118                return;
 119        }
 120
 121        if (skb && (skb->len > 0))
 122                skb_copy_from_linear_data(skb, skb_put(nskb, skb->len), skb->len);
 123
 124        memcpy(skb_put(nskb, count), buf, count);
 125
 126        session->reassembly[id] = nskb;
 127
 128        kfree_skb(skb);
 129}
 130
 131static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
 132{
 133        __u8 hdr, hdrlen, id;
 134        __u16 len;
 135
 136        BT_DBG("session %p skb %p len %d", session, skb, skb->len);
 137
 138        while (skb->len > 0) {
 139                hdr = skb->data[0];
 140
 141                switch (hdr & 0xc0) {
 142                case 0x40:
 143                        hdrlen = 2;
 144                        len = skb->data[1];
 145                        break;
 146                case 0x80:
 147                        hdrlen = 3;
 148                        len = skb->data[1] | (skb->data[2] << 8);
 149                        break;
 150                default:
 151                        hdrlen = 1;
 152                        len = 0;
 153                        break;
 154                }
 155
 156                id = (hdr & 0x3c) >> 2;
 157
 158                BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
 159
 160                if (hdrlen + len > skb->len) {
 161                        BT_ERR("Wrong size or header information in CMTP frame");
 162                        break;
 163                }
 164
 165                if (len == 0) {
 166                        skb_pull(skb, hdrlen);
 167                        continue;
 168                }
 169
 170                switch (hdr & 0x03) {
 171                case 0x00:
 172                        cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
 173                        cmtp_recv_capimsg(session, session->reassembly[id]);
 174                        session->reassembly[id] = NULL;
 175                        break;
 176                case 0x01:
 177                        cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
 178                        break;
 179                default:
 180                        if (session->reassembly[id] != NULL)
 181                                kfree_skb(session->reassembly[id]);
 182                        session->reassembly[id] = NULL;
 183                        break;
 184                }
 185
 186                skb_pull(skb, hdrlen + len);
 187        }
 188
 189        kfree_skb(skb);
 190        return 0;
 191}
 192
 193static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
 194{
 195        struct socket *sock = session->sock;
 196        struct kvec iv = { data, len };
 197        struct msghdr msg;
 198
 199        BT_DBG("session %p data %p len %d", session, data, len);
 200
 201        if (!len)
 202                return 0;
 203
 204        memset(&msg, 0, sizeof(msg));
 205
 206        return kernel_sendmsg(sock, &msg, &iv, 1, len);
 207}
 208
 209static void cmtp_process_transmit(struct cmtp_session *session)
 210{
 211        struct sk_buff *skb, *nskb;
 212        unsigned char *hdr;
 213        unsigned int size, tail;
 214
 215        BT_DBG("session %p", session);
 216
 217        nskb = alloc_skb(session->mtu, GFP_ATOMIC);
 218        if (!nskb) {
 219                BT_ERR("Can't allocate memory for new frame");
 220                return;
 221        }
 222
 223        while ((skb = skb_dequeue(&session->transmit))) {
 224                struct cmtp_scb *scb = (void *) skb->cb;
 225
 226                tail = session->mtu - nskb->len;
 227                if (tail < 5) {
 228                        cmtp_send_frame(session, nskb->data, nskb->len);
 229                        skb_trim(nskb, 0);
 230                        tail = session->mtu;
 231                }
 232
 233                size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
 234
 235                if (scb->id < 0) {
 236                        scb->id = cmtp_alloc_block_id(session);
 237                        if (scb->id < 0) {
 238                                skb_queue_head(&session->transmit, skb);
 239                                break;
 240                        }
 241                }
 242
 243                if (size < 256) {
 244                        hdr = skb_put(nskb, 2);
 245                        hdr[0] = 0x40
 246                                | ((scb->id << 2) & 0x3c)
 247                                | ((skb->len == size) ? 0x00 : 0x01);
 248                        hdr[1] = size;
 249                } else {
 250                        hdr = skb_put(nskb, 3);
 251                        hdr[0] = 0x80
 252                                | ((scb->id << 2) & 0x3c)
 253                                | ((skb->len == size) ? 0x00 : 0x01);
 254                        hdr[1] = size & 0xff;
 255                        hdr[2] = size >> 8;
 256                }
 257
 258                skb_copy_from_linear_data(skb, skb_put(nskb, size), size);
 259                skb_pull(skb, size);
 260
 261                if (skb->len > 0) {
 262                        skb_queue_head(&session->transmit, skb);
 263                } else {
 264                        cmtp_free_block_id(session, scb->id);
 265                        if (scb->data) {
 266                                cmtp_send_frame(session, nskb->data, nskb->len);
 267                                skb_trim(nskb, 0);
 268                        }
 269                        kfree_skb(skb);
 270                }
 271        }
 272
 273        cmtp_send_frame(session, nskb->data, nskb->len);
 274
 275        kfree_skb(nskb);
 276}
 277
 278static int cmtp_session(void *arg)
 279{
 280        struct cmtp_session *session = arg;
 281        struct sock *sk = session->sock->sk;
 282        struct sk_buff *skb;
 283        wait_queue_t wait;
 284
 285        BT_DBG("session %p", session);
 286
 287        set_user_nice(current, -15);
 288
 289        init_waitqueue_entry(&wait, current);
 290        add_wait_queue(sk_sleep(sk), &wait);
 291        while (1) {
 292                set_current_state(TASK_INTERRUPTIBLE);
 293
 294                if (atomic_read(&session->terminate))
 295                        break;
 296                if (sk->sk_state != BT_CONNECTED)
 297                        break;
 298
 299                while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
 300                        skb_orphan(skb);
 301                        if (!skb_linearize(skb))
 302                                cmtp_recv_frame(session, skb);
 303                        else
 304                                kfree_skb(skb);
 305                }
 306
 307                cmtp_process_transmit(session);
 308
 309                schedule();
 310        }
 311        __set_current_state(TASK_RUNNING);
 312        remove_wait_queue(sk_sleep(sk), &wait);
 313
 314        down_write(&cmtp_session_sem);
 315
 316        if (!(session->flags & (1 << CMTP_LOOPBACK)))
 317                cmtp_detach_device(session);
 318
 319        fput(session->sock->file);
 320
 321        __cmtp_unlink_session(session);
 322
 323        up_write(&cmtp_session_sem);
 324
 325        kfree(session);
 326        module_put_and_exit(0);
 327        return 0;
 328}
 329
 330int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
 331{
 332        struct cmtp_session *session, *s;
 333        int i, err;
 334
 335        BT_DBG("");
 336
 337        session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL);
 338        if (!session)
 339                return -ENOMEM;
 340
 341        down_write(&cmtp_session_sem);
 342
 343        s = __cmtp_get_session(&bt_sk(sock->sk)->dst);
 344        if (s && s->state == BT_CONNECTED) {
 345                err = -EEXIST;
 346                goto failed;
 347        }
 348
 349        bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst);
 350
 351        session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu,
 352                                        l2cap_pi(sock->sk)->chan->imtu);
 353
 354        BT_DBG("mtu %d", session->mtu);
 355
 356        sprintf(session->name, "%pMR", &bt_sk(sock->sk)->dst);
 357
 358        session->sock  = sock;
 359        session->state = BT_CONFIG;
 360
 361        init_waitqueue_head(&session->wait);
 362
 363        session->msgnum = CMTP_INITIAL_MSGNUM;
 364
 365        INIT_LIST_HEAD(&session->applications);
 366
 367        skb_queue_head_init(&session->transmit);
 368
 369        for (i = 0; i < 16; i++)
 370                session->reassembly[i] = NULL;
 371
 372        session->flags = req->flags;
 373
 374        __cmtp_link_session(session);
 375
 376        __module_get(THIS_MODULE);
 377        session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d",
 378                                                                session->num);
 379        if (IS_ERR(session->task)) {
 380                module_put(THIS_MODULE);
 381                err = PTR_ERR(session->task);
 382                goto unlink;
 383        }
 384
 385        if (!(session->flags & (1 << CMTP_LOOPBACK))) {
 386                err = cmtp_attach_device(session);
 387                if (err < 0) {
 388                        atomic_inc(&session->terminate);
 389                        wake_up_process(session->task);
 390                        up_write(&cmtp_session_sem);
 391                        return err;
 392                }
 393        }
 394
 395        up_write(&cmtp_session_sem);
 396        return 0;
 397
 398unlink:
 399        __cmtp_unlink_session(session);
 400
 401failed:
 402        up_write(&cmtp_session_sem);
 403        kfree(session);
 404        return err;
 405}
 406
 407int cmtp_del_connection(struct cmtp_conndel_req *req)
 408{
 409        struct cmtp_session *session;
 410        int err = 0;
 411
 412        BT_DBG("");
 413
 414        down_read(&cmtp_session_sem);
 415
 416        session = __cmtp_get_session(&req->bdaddr);
 417        if (session) {
 418                /* Flush the transmit queue */
 419                skb_queue_purge(&session->transmit);
 420
 421                /* Stop session thread */
 422                atomic_inc(&session->terminate);
 423                wake_up_process(session->task);
 424        } else
 425                err = -ENOENT;
 426
 427        up_read(&cmtp_session_sem);
 428        return err;
 429}
 430
 431int cmtp_get_connlist(struct cmtp_connlist_req *req)
 432{
 433        struct cmtp_session *session;
 434        int err = 0, n = 0;
 435
 436        BT_DBG("");
 437
 438        down_read(&cmtp_session_sem);
 439
 440        list_for_each_entry(session, &cmtp_session_list, list) {
 441                struct cmtp_conninfo ci;
 442
 443                __cmtp_copy_session(session, &ci);
 444
 445                if (copy_to_user(req->ci, &ci, sizeof(ci))) {
 446                        err = -EFAULT;
 447                        break;
 448                }
 449
 450                if (++n >= req->cnum)
 451                        break;
 452
 453                req->ci++;
 454        }
 455        req->cnum = n;
 456
 457        up_read(&cmtp_session_sem);
 458        return err;
 459}
 460
 461int cmtp_get_conninfo(struct cmtp_conninfo *ci)
 462{
 463        struct cmtp_session *session;
 464        int err = 0;
 465
 466        down_read(&cmtp_session_sem);
 467
 468        session = __cmtp_get_session(&ci->bdaddr);
 469        if (session)
 470                __cmtp_copy_session(session, ci);
 471        else
 472                err = -ENOENT;
 473
 474        up_read(&cmtp_session_sem);
 475        return err;
 476}
 477
 478
 479static int __init cmtp_init(void)
 480{
 481        BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION);
 482
 483        cmtp_init_sockets();
 484
 485        return 0;
 486}
 487
 488static void __exit cmtp_exit(void)
 489{
 490        cmtp_cleanup_sockets();
 491}
 492
 493module_init(cmtp_init);
 494module_exit(cmtp_exit);
 495
 496MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 497MODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION);
 498MODULE_VERSION(VERSION);
 499MODULE_LICENSE("GPL");
 500MODULE_ALIAS("bt-proto-5");
 501