linux/net/bluetooth/cmtp/capi.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/export.h>
  24#include <linux/proc_fs.h>
  25#include <linux/seq_file.h>
  26#include <linux/types.h>
  27#include <linux/errno.h>
  28#include <linux/kernel.h>
  29#include <linux/sched.h>
  30#include <linux/slab.h>
  31#include <linux/poll.h>
  32#include <linux/fcntl.h>
  33#include <linux/skbuff.h>
  34#include <linux/socket.h>
  35#include <linux/ioctl.h>
  36#include <linux/file.h>
  37#include <linux/wait.h>
  38#include <linux/kthread.h>
  39#include <net/sock.h>
  40
  41#include <linux/isdn/capilli.h>
  42#include <linux/isdn/capicmd.h>
  43#include <linux/isdn/capiutil.h>
  44
  45#include "cmtp.h"
  46
  47#define CAPI_INTEROPERABILITY           0x20
  48
  49#define CAPI_INTEROPERABILITY_REQ       CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
  50#define CAPI_INTEROPERABILITY_CONF      CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
  51#define CAPI_INTEROPERABILITY_IND       CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
  52#define CAPI_INTEROPERABILITY_RESP      CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
  53
  54#define CAPI_INTEROPERABILITY_REQ_LEN   (CAPI_MSG_BASELEN + 2)
  55#define CAPI_INTEROPERABILITY_CONF_LEN  (CAPI_MSG_BASELEN + 4)
  56#define CAPI_INTEROPERABILITY_IND_LEN   (CAPI_MSG_BASELEN + 2)
  57#define CAPI_INTEROPERABILITY_RESP_LEN  (CAPI_MSG_BASELEN + 2)
  58
  59#define CAPI_FUNCTION_REGISTER          0
  60#define CAPI_FUNCTION_RELEASE           1
  61#define CAPI_FUNCTION_GET_PROFILE       2
  62#define CAPI_FUNCTION_GET_MANUFACTURER  3
  63#define CAPI_FUNCTION_GET_VERSION       4
  64#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
  65#define CAPI_FUNCTION_MANUFACTURER      6
  66#define CAPI_FUNCTION_LOOPBACK          7
  67
  68
  69#define CMTP_MSGNUM     1
  70#define CMTP_APPLID     2
  71#define CMTP_MAPPING    3
  72
  73static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
  74{
  75        struct cmtp_application *app = kzalloc(sizeof(*app), GFP_KERNEL);
  76
  77        BT_DBG("session %p application %p appl %d", session, app, appl);
  78
  79        if (!app)
  80                return NULL;
  81
  82        app->state = BT_OPEN;
  83        app->appl = appl;
  84
  85        list_add_tail(&app->list, &session->applications);
  86
  87        return app;
  88}
  89
  90static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
  91{
  92        BT_DBG("session %p application %p", session, app);
  93
  94        if (app) {
  95                list_del(&app->list);
  96                kfree(app);
  97        }
  98}
  99
 100static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
 101{
 102        struct cmtp_application *app;
 103        struct list_head *p, *n;
 104
 105        list_for_each_safe(p, n, &session->applications) {
 106                app = list_entry(p, struct cmtp_application, list);
 107                switch (pattern) {
 108                case CMTP_MSGNUM:
 109                        if (app->msgnum == value)
 110                                return app;
 111                        break;
 112                case CMTP_APPLID:
 113                        if (app->appl == value)
 114                                return app;
 115                        break;
 116                case CMTP_MAPPING:
 117                        if (app->mapping == value)
 118                                return app;
 119                        break;
 120                }
 121        }
 122
 123        return NULL;
 124}
 125
 126static int cmtp_msgnum_get(struct cmtp_session *session)
 127{
 128        session->msgnum++;
 129
 130        if ((session->msgnum & 0xff) > 200)
 131                session->msgnum = CMTP_INITIAL_MSGNUM + 1;
 132
 133        return session->msgnum;
 134}
 135
 136static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
 137{
 138        struct cmtp_scb *scb = (void *) skb->cb;
 139
 140        BT_DBG("session %p skb %p len %d", session, skb, skb->len);
 141
 142        scb->id = -1;
 143        scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
 144
 145        skb_queue_tail(&session->transmit, skb);
 146
 147        wake_up_interruptible(sk_sleep(session->sock->sk));
 148}
 149
 150static void cmtp_send_interopmsg(struct cmtp_session *session,
 151                                        __u8 subcmd, __u16 appl, __u16 msgnum,
 152                                        __u16 function, unsigned char *buf, int len)
 153{
 154        struct sk_buff *skb;
 155        unsigned char *s;
 156
 157        BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
 158
 159        skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC);
 160        if (!skb) {
 161                BT_ERR("Can't allocate memory for interoperability packet");
 162                return;
 163        }
 164
 165        s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
 166
 167        capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
 168        capimsg_setu16(s, 2, appl);
 169        capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
 170        capimsg_setu8 (s, 5, subcmd);
 171        capimsg_setu16(s, 6, msgnum);
 172
 173        /* Interoperability selector (Bluetooth Device Management) */
 174        capimsg_setu16(s, 8, 0x0001);
 175
 176        capimsg_setu8 (s, 10, 3 + len);
 177        capimsg_setu16(s, 11, function);
 178        capimsg_setu8 (s, 13, len);
 179
 180        if (len > 0)
 181                memcpy(s + 14, buf, len);
 182
 183        cmtp_send_capimsg(session, skb);
 184}
 185
 186static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
 187{
 188        struct capi_ctr *ctrl = &session->ctrl;
 189        struct cmtp_application *application;
 190        __u16 appl, msgnum, func, info;
 191        __u32 controller;
 192
 193        BT_DBG("session %p skb %p len %d", session, skb, skb->len);
 194
 195        switch (CAPIMSG_SUBCOMMAND(skb->data)) {
 196        case CAPI_CONF:
 197                if (skb->len < CAPI_MSG_BASELEN + 10)
 198                        break;
 199
 200                func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
 201                info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
 202
 203                switch (func) {
 204                case CAPI_FUNCTION_REGISTER:
 205                        msgnum = CAPIMSG_MSGID(skb->data);
 206
 207                        application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
 208                        if (application) {
 209                                application->state = BT_CONNECTED;
 210                                application->msgnum = 0;
 211                                application->mapping = CAPIMSG_APPID(skb->data);
 212                                wake_up_interruptible(&session->wait);
 213                        }
 214
 215                        break;
 216
 217                case CAPI_FUNCTION_RELEASE:
 218                        appl = CAPIMSG_APPID(skb->data);
 219
 220                        application = cmtp_application_get(session, CMTP_MAPPING, appl);
 221                        if (application) {
 222                                application->state = BT_CLOSED;
 223                                application->msgnum = 0;
 224                                wake_up_interruptible(&session->wait);
 225                        }
 226
 227                        break;
 228
 229                case CAPI_FUNCTION_GET_PROFILE:
 230                        if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile))
 231                                break;
 232
 233                        controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
 234                        msgnum = CAPIMSG_MSGID(skb->data);
 235
 236                        if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
 237                                session->ncontroller = controller;
 238                                wake_up_interruptible(&session->wait);
 239                                break;
 240                        }
 241
 242                        if (!info && ctrl) {
 243                                memcpy(&ctrl->profile,
 244                                        skb->data + CAPI_MSG_BASELEN + 11,
 245                                        sizeof(capi_profile));
 246                                session->state = BT_CONNECTED;
 247                                capi_ctr_ready(ctrl);
 248                        }
 249
 250                        break;
 251
 252                case CAPI_FUNCTION_GET_MANUFACTURER:
 253                        if (skb->len < CAPI_MSG_BASELEN + 15)
 254                                break;
 255
 256                        controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
 257
 258                        if (!info && ctrl) {
 259                                int len = min_t(uint, CAPI_MANUFACTURER_LEN,
 260                                                skb->data[CAPI_MSG_BASELEN + 14]);
 261
 262                                memset(ctrl->manu, 0, CAPI_MANUFACTURER_LEN);
 263                                strncpy(ctrl->manu,
 264                                        skb->data + CAPI_MSG_BASELEN + 15, len);
 265                        }
 266
 267                        break;
 268
 269                case CAPI_FUNCTION_GET_VERSION:
 270                        if (skb->len < CAPI_MSG_BASELEN + 32)
 271                                break;
 272
 273                        controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
 274
 275                        if (!info && ctrl) {
 276                                ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
 277                                ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
 278                                ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
 279                                ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
 280                        }
 281
 282                        break;
 283
 284                case CAPI_FUNCTION_GET_SERIAL_NUMBER:
 285                        if (skb->len < CAPI_MSG_BASELEN + 17)
 286                                break;
 287
 288                        controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
 289
 290                        if (!info && ctrl) {
 291                                int len = min_t(uint, CAPI_SERIAL_LEN,
 292                                                skb->data[CAPI_MSG_BASELEN + 16]);
 293
 294                                memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
 295                                strncpy(ctrl->serial,
 296                                        skb->data + CAPI_MSG_BASELEN + 17, len);
 297                        }
 298
 299                        break;
 300                }
 301
 302                break;
 303
 304        case CAPI_IND:
 305                if (skb->len < CAPI_MSG_BASELEN + 6)
 306                        break;
 307
 308                func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
 309
 310                if (func == CAPI_FUNCTION_LOOPBACK) {
 311                        int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6,
 312                                                skb->data[CAPI_MSG_BASELEN + 5]);
 313                        appl = CAPIMSG_APPID(skb->data);
 314                        msgnum = CAPIMSG_MSGID(skb->data);
 315                        cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
 316                                                skb->data + CAPI_MSG_BASELEN + 6, len);
 317                }
 318
 319                break;
 320        }
 321
 322        kfree_skb(skb);
 323}
 324
 325void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
 326{
 327        struct capi_ctr *ctrl = &session->ctrl;
 328        struct cmtp_application *application;
 329        __u16 appl;
 330        __u32 contr;
 331
 332        BT_DBG("session %p skb %p len %d", session, skb, skb->len);
 333
 334        if (skb->len < CAPI_MSG_BASELEN)
 335                return;
 336
 337        if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
 338                cmtp_recv_interopmsg(session, skb);
 339                return;
 340        }
 341
 342        if (session->flags & (1 << CMTP_LOOPBACK)) {
 343                kfree_skb(skb);
 344                return;
 345        }
 346
 347        appl = CAPIMSG_APPID(skb->data);
 348        contr = CAPIMSG_CONTROL(skb->data);
 349
 350        application = cmtp_application_get(session, CMTP_MAPPING, appl);
 351        if (application) {
 352                appl = application->appl;
 353                CAPIMSG_SETAPPID(skb->data, appl);
 354        } else {
 355                BT_ERR("Can't find application with id %d", appl);
 356                kfree_skb(skb);
 357                return;
 358        }
 359
 360        if ((contr & 0x7f) == 0x01) {
 361                contr = (contr & 0xffffff80) | session->num;
 362                CAPIMSG_SETCONTROL(skb->data, contr);
 363        }
 364
 365        if (!ctrl) {
 366                BT_ERR("Can't find controller %d for message", session->num);
 367                kfree_skb(skb);
 368                return;
 369        }
 370
 371        capi_ctr_handle_message(ctrl, appl, skb);
 372}
 373
 374static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
 375{
 376        BT_DBG("ctrl %p data %p", ctrl, data);
 377
 378        return 0;
 379}
 380
 381static void cmtp_reset_ctr(struct capi_ctr *ctrl)
 382{
 383        struct cmtp_session *session = ctrl->driverdata;
 384
 385        BT_DBG("ctrl %p", ctrl);
 386
 387        capi_ctr_down(ctrl);
 388
 389        atomic_inc(&session->terminate);
 390        wake_up_process(session->task);
 391}
 392
 393static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
 394{
 395        DECLARE_WAITQUEUE(wait, current);
 396        struct cmtp_session *session = ctrl->driverdata;
 397        struct cmtp_application *application;
 398        unsigned long timeo = CMTP_INTEROP_TIMEOUT;
 399        unsigned char buf[8];
 400        int err = 0, nconn, want = rp->level3cnt;
 401
 402        BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
 403                ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
 404
 405        application = cmtp_application_add(session, appl);
 406        if (!application) {
 407                BT_ERR("Can't allocate memory for new application");
 408                return;
 409        }
 410
 411        if (want < 0)
 412                nconn = ctrl->profile.nbchannel * -want;
 413        else
 414                nconn = want;
 415
 416        if (nconn == 0)
 417                nconn = ctrl->profile.nbchannel;
 418
 419        capimsg_setu16(buf, 0, nconn);
 420        capimsg_setu16(buf, 2, rp->datablkcnt);
 421        capimsg_setu16(buf, 4, rp->datablklen);
 422
 423        application->state = BT_CONFIG;
 424        application->msgnum = cmtp_msgnum_get(session);
 425
 426        cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
 427                                CAPI_FUNCTION_REGISTER, buf, 6);
 428
 429        add_wait_queue(&session->wait, &wait);
 430        while (1) {
 431                set_current_state(TASK_INTERRUPTIBLE);
 432
 433                if (!timeo) {
 434                        err = -EAGAIN;
 435                        break;
 436                }
 437
 438                if (application->state == BT_CLOSED) {
 439                        err = -application->err;
 440                        break;
 441                }
 442
 443                if (application->state == BT_CONNECTED)
 444                        break;
 445
 446                if (signal_pending(current)) {
 447                        err = -EINTR;
 448                        break;
 449                }
 450
 451                timeo = schedule_timeout(timeo);
 452        }
 453        set_current_state(TASK_RUNNING);
 454        remove_wait_queue(&session->wait, &wait);
 455
 456        if (err) {
 457                cmtp_application_del(session, application);
 458                return;
 459        }
 460}
 461
 462static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
 463{
 464        struct cmtp_session *session = ctrl->driverdata;
 465        struct cmtp_application *application;
 466
 467        BT_DBG("ctrl %p appl %d", ctrl, appl);
 468
 469        application = cmtp_application_get(session, CMTP_APPLID, appl);
 470        if (!application) {
 471                BT_ERR("Can't find application");
 472                return;
 473        }
 474
 475        application->msgnum = cmtp_msgnum_get(session);
 476
 477        cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
 478                                CAPI_FUNCTION_RELEASE, NULL, 0);
 479
 480        wait_event_interruptible_timeout(session->wait,
 481                        (application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT);
 482
 483        cmtp_application_del(session, application);
 484}
 485
 486static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
 487{
 488        struct cmtp_session *session = ctrl->driverdata;
 489        struct cmtp_application *application;
 490        __u16 appl;
 491        __u32 contr;
 492
 493        BT_DBG("ctrl %p skb %p", ctrl, skb);
 494
 495        appl = CAPIMSG_APPID(skb->data);
 496        contr = CAPIMSG_CONTROL(skb->data);
 497
 498        application = cmtp_application_get(session, CMTP_APPLID, appl);
 499        if ((!application) || (application->state != BT_CONNECTED)) {
 500                BT_ERR("Can't find application with id %d", appl);
 501                return CAPI_ILLAPPNR;
 502        }
 503
 504        CAPIMSG_SETAPPID(skb->data, application->mapping);
 505
 506        if ((contr & 0x7f) == session->num) {
 507                contr = (contr & 0xffffff80) | 0x01;
 508                CAPIMSG_SETCONTROL(skb->data, contr);
 509        }
 510
 511        cmtp_send_capimsg(session, skb);
 512
 513        return CAPI_NOERROR;
 514}
 515
 516static char *cmtp_procinfo(struct capi_ctr *ctrl)
 517{
 518        return "CAPI Message Transport Protocol";
 519}
 520
 521static int cmtp_proc_show(struct seq_file *m, void *v)
 522{
 523        struct capi_ctr *ctrl = m->private;
 524        struct cmtp_session *session = ctrl->driverdata;
 525        struct cmtp_application *app;
 526        struct list_head *p, *n;
 527
 528        seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl));
 529        seq_printf(m, "addr %s\n", session->name);
 530        seq_printf(m, "ctrl %d\n", session->num);
 531
 532        list_for_each_safe(p, n, &session->applications) {
 533                app = list_entry(p, struct cmtp_application, list);
 534                seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping);
 535        }
 536
 537        return 0;
 538}
 539
 540static int cmtp_proc_open(struct inode *inode, struct file *file)
 541{
 542        return single_open(file, cmtp_proc_show, PDE_DATA(inode));
 543}
 544
 545static const struct file_operations cmtp_proc_fops = {
 546        .owner          = THIS_MODULE,
 547        .open           = cmtp_proc_open,
 548        .read           = seq_read,
 549        .llseek         = seq_lseek,
 550        .release        = single_release,
 551};
 552
 553int cmtp_attach_device(struct cmtp_session *session)
 554{
 555        unsigned char buf[4];
 556        long ret;
 557
 558        BT_DBG("session %p", session);
 559
 560        capimsg_setu32(buf, 0, 0);
 561
 562        cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
 563                                CAPI_FUNCTION_GET_PROFILE, buf, 4);
 564
 565        ret = wait_event_interruptible_timeout(session->wait,
 566                        session->ncontroller, CMTP_INTEROP_TIMEOUT);
 567
 568        BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
 569
 570        if (!ret)
 571                return -ETIMEDOUT;
 572
 573        if (!session->ncontroller)
 574                return -ENODEV;
 575
 576        if (session->ncontroller > 1)
 577                BT_INFO("Setting up only CAPI controller 1");
 578
 579        session->ctrl.owner      = THIS_MODULE;
 580        session->ctrl.driverdata = session;
 581        strcpy(session->ctrl.name, session->name);
 582
 583        session->ctrl.driver_name   = "cmtp";
 584        session->ctrl.load_firmware = cmtp_load_firmware;
 585        session->ctrl.reset_ctr     = cmtp_reset_ctr;
 586        session->ctrl.register_appl = cmtp_register_appl;
 587        session->ctrl.release_appl  = cmtp_release_appl;
 588        session->ctrl.send_message  = cmtp_send_message;
 589
 590        session->ctrl.procinfo      = cmtp_procinfo;
 591        session->ctrl.proc_fops = &cmtp_proc_fops;
 592
 593        if (attach_capi_ctr(&session->ctrl) < 0) {
 594                BT_ERR("Can't attach new controller");
 595                return -EBUSY;
 596        }
 597
 598        session->num = session->ctrl.cnr;
 599
 600        BT_DBG("session %p num %d", session, session->num);
 601
 602        capimsg_setu32(buf, 0, 1);
 603
 604        cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
 605                                CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
 606
 607        cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
 608                                CAPI_FUNCTION_GET_VERSION, buf, 4);
 609
 610        cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
 611                                CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
 612
 613        cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
 614                                CAPI_FUNCTION_GET_PROFILE, buf, 4);
 615
 616        return 0;
 617}
 618
 619void cmtp_detach_device(struct cmtp_session *session)
 620{
 621        BT_DBG("session %p", session);
 622
 623        detach_capi_ctr(&session->ctrl);
 624}
 625