wl1251: prevent scan when connected
[pandora-wifi.git] / net / bluetooth / cmtp / capi.c
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 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34))
25 #include <linux/proc_fs.h>
26 #include <linux/seq_file.h>
27 #endif
28 #include <linux/types.h>
29 #include <linux/errno.h>
30 #include <linux/kernel.h>
31 #include <linux/sched.h>
32 #include <linux/slab.h>
33 #include <linux/poll.h>
34 #include <linux/fcntl.h>
35 #include <linux/skbuff.h>
36 #include <linux/socket.h>
37 #include <linux/ioctl.h>
38 #include <linux/file.h>
39 #include <linux/wait.h>
40 #include <net/sock.h>
41
42 #include <linux/isdn/capilli.h>
43 #include <linux/isdn/capicmd.h>
44 #include <linux/isdn/capiutil.h>
45
46 #include "cmtp.h"
47
48 #define CAPI_INTEROPERABILITY           0x20
49
50 #define CAPI_INTEROPERABILITY_REQ       CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
51 #define CAPI_INTEROPERABILITY_CONF      CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
52 #define CAPI_INTEROPERABILITY_IND       CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
53 #define CAPI_INTEROPERABILITY_RESP      CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
54
55 #define CAPI_INTEROPERABILITY_REQ_LEN   (CAPI_MSG_BASELEN + 2)
56 #define CAPI_INTEROPERABILITY_CONF_LEN  (CAPI_MSG_BASELEN + 4)
57 #define CAPI_INTEROPERABILITY_IND_LEN   (CAPI_MSG_BASELEN + 2)
58 #define CAPI_INTEROPERABILITY_RESP_LEN  (CAPI_MSG_BASELEN + 2)
59
60 #define CAPI_FUNCTION_REGISTER          0
61 #define CAPI_FUNCTION_RELEASE           1
62 #define CAPI_FUNCTION_GET_PROFILE       2
63 #define CAPI_FUNCTION_GET_MANUFACTURER  3
64 #define CAPI_FUNCTION_GET_VERSION       4
65 #define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
66 #define CAPI_FUNCTION_MANUFACTURER      6
67 #define CAPI_FUNCTION_LOOPBACK          7
68
69
70 #define CMTP_MSGNUM     1
71 #define CMTP_APPLID     2
72 #define CMTP_MAPPING    3
73
74 static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
75 {
76         struct cmtp_application *app = kzalloc(sizeof(*app), GFP_KERNEL);
77
78         BT_DBG("session %p application %p appl %d", session, app, appl);
79
80         if (!app)
81                 return NULL;
82
83         app->state = BT_OPEN;
84         app->appl = appl;
85
86         list_add_tail(&app->list, &session->applications);
87
88         return app;
89 }
90
91 static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
92 {
93         BT_DBG("session %p application %p", session, app);
94
95         if (app) {
96                 list_del(&app->list);
97                 kfree(app);
98         }
99 }
100
101 static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
102 {
103         struct cmtp_application *app;
104         struct list_head *p, *n;
105
106         list_for_each_safe(p, n, &session->applications) {
107                 app = list_entry(p, struct cmtp_application, list);
108                 switch (pattern) {
109                 case CMTP_MSGNUM:
110                         if (app->msgnum == value)
111                                 return app;
112                         break;
113                 case CMTP_APPLID:
114                         if (app->appl == value)
115                                 return app;
116                         break;
117                 case CMTP_MAPPING:
118                         if (app->mapping == value)
119                                 return app;
120                         break;
121                 }
122         }
123
124         return NULL;
125 }
126
127 static int cmtp_msgnum_get(struct cmtp_session *session)
128 {
129         session->msgnum++;
130
131         if ((session->msgnum & 0xff) > 200)
132                 session->msgnum = CMTP_INITIAL_MSGNUM + 1;
133
134         return session->msgnum;
135 }
136
137 static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
138 {
139         struct cmtp_scb *scb = (void *) skb->cb;
140
141         BT_DBG("session %p skb %p len %d", session, skb, skb->len);
142
143         scb->id = -1;
144         scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
145
146         skb_queue_tail(&session->transmit, skb);
147
148         cmtp_schedule(session);
149 }
150
151 static void cmtp_send_interopmsg(struct cmtp_session *session,
152                                         __u8 subcmd, __u16 appl, __u16 msgnum,
153                                         __u16 function, unsigned char *buf, int len)
154 {
155         struct sk_buff *skb;
156         unsigned char *s;
157
158         BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
159
160         if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) {
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
186 static 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
325 void 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 cmd, 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         cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
348         appl = CAPIMSG_APPID(skb->data);
349         contr = CAPIMSG_CONTROL(skb->data);
350
351         application = cmtp_application_get(session, CMTP_MAPPING, appl);
352         if (application) {
353                 appl = application->appl;
354                 CAPIMSG_SETAPPID(skb->data, appl);
355         } else {
356                 BT_ERR("Can't find application with id %d", appl);
357                 kfree_skb(skb);
358                 return;
359         }
360
361         if ((contr & 0x7f) == 0x01) {
362                 contr = (contr & 0xffffff80) | session->num;
363                 CAPIMSG_SETCONTROL(skb->data, contr);
364         }
365
366         if (!ctrl) {
367                 BT_ERR("Can't find controller %d for message", session->num);
368                 kfree_skb(skb);
369                 return;
370         }
371
372         capi_ctr_handle_message(ctrl, appl, skb);
373 }
374
375 static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
376 {
377         BT_DBG("ctrl %p data %p", ctrl, data);
378
379         return 0;
380 }
381
382 static void cmtp_reset_ctr(struct capi_ctr *ctrl)
383 {
384         struct cmtp_session *session = ctrl->driverdata;
385
386         BT_DBG("ctrl %p", ctrl);
387
388 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
389         capi_ctr_down(ctrl);
390 #else
391         capi_ctr_reseted(ctrl);
392 #endif
393
394         atomic_inc(&session->terminate);
395         cmtp_schedule(session);
396 }
397
398 static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
399 {
400         DECLARE_WAITQUEUE(wait, current);
401         struct cmtp_session *session = ctrl->driverdata;
402         struct cmtp_application *application;
403         unsigned long timeo = CMTP_INTEROP_TIMEOUT;
404         unsigned char buf[8];
405         int err = 0, nconn, want = rp->level3cnt;
406
407         BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
408                 ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
409
410         application = cmtp_application_add(session, appl);
411         if (!application) {
412                 BT_ERR("Can't allocate memory for new application");
413                 return;
414         }
415
416         if (want < 0)
417                 nconn = ctrl->profile.nbchannel * -want;
418         else
419                 nconn = want;
420
421         if (nconn == 0)
422                 nconn = ctrl->profile.nbchannel;
423
424         capimsg_setu16(buf, 0, nconn);
425         capimsg_setu16(buf, 2, rp->datablkcnt);
426         capimsg_setu16(buf, 4, rp->datablklen);
427
428         application->state = BT_CONFIG;
429         application->msgnum = cmtp_msgnum_get(session);
430
431         cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
432                                 CAPI_FUNCTION_REGISTER, buf, 6);
433
434         add_wait_queue(&session->wait, &wait);
435         while (1) {
436                 set_current_state(TASK_INTERRUPTIBLE);
437
438                 if (!timeo) {
439                         err = -EAGAIN;
440                         break;
441                 }
442
443                 if (application->state == BT_CLOSED) {
444                         err = -application->err;
445                         break;
446                 }
447
448                 if (application->state == BT_CONNECTED)
449                         break;
450
451                 if (signal_pending(current)) {
452                         err = -EINTR;
453                         break;
454                 }
455
456                 timeo = schedule_timeout(timeo);
457         }
458         set_current_state(TASK_RUNNING);
459         remove_wait_queue(&session->wait, &wait);
460
461         if (err) {
462                 cmtp_application_del(session, application);
463                 return;
464         }
465 }
466
467 static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
468 {
469         struct cmtp_session *session = ctrl->driverdata;
470         struct cmtp_application *application;
471
472         BT_DBG("ctrl %p appl %d", ctrl, appl);
473
474         application = cmtp_application_get(session, CMTP_APPLID, appl);
475         if (!application) {
476                 BT_ERR("Can't find application");
477                 return;
478         }
479
480         application->msgnum = cmtp_msgnum_get(session);
481
482         cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
483                                 CAPI_FUNCTION_RELEASE, NULL, 0);
484
485         wait_event_interruptible_timeout(session->wait,
486                         (application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT);
487
488         cmtp_application_del(session, application);
489 }
490
491 static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
492 {
493         struct cmtp_session *session = ctrl->driverdata;
494         struct cmtp_application *application;
495         __u16 appl;
496         __u32 contr;
497
498         BT_DBG("ctrl %p skb %p", ctrl, skb);
499
500         appl = CAPIMSG_APPID(skb->data);
501         contr = CAPIMSG_CONTROL(skb->data);
502
503         application = cmtp_application_get(session, CMTP_APPLID, appl);
504         if ((!application) || (application->state != BT_CONNECTED)) {
505                 BT_ERR("Can't find application with id %d", appl);
506                 return CAPI_ILLAPPNR;
507         }
508
509         CAPIMSG_SETAPPID(skb->data, application->mapping);
510
511         if ((contr & 0x7f) == session->num) {
512                 contr = (contr & 0xffffff80) | 0x01;
513                 CAPIMSG_SETCONTROL(skb->data, contr);
514         }
515
516         cmtp_send_capimsg(session, skb);
517
518         return CAPI_NOERROR;
519 }
520
521 static char *cmtp_procinfo(struct capi_ctr *ctrl)
522 {
523         return "CAPI Message Transport Protocol";
524 }
525
526 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34))
527 static int cmtp_proc_show(struct seq_file *m, void *v)
528 {
529         struct capi_ctr *ctrl = m->private;
530         struct cmtp_session *session = ctrl->driverdata;
531         struct cmtp_application *app;
532         struct list_head *p, *n;
533
534         seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl));
535         seq_printf(m, "addr %s\n", session->name);
536         seq_printf(m, "ctrl %d\n", session->num);
537
538         list_for_each_safe(p, n, &session->applications) {
539                 app = list_entry(p, struct cmtp_application, list);
540                 seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping);
541         }
542
543         return 0;
544 }
545
546 static int cmtp_proc_open(struct inode *inode, struct file *file)
547 {
548         return single_open(file, cmtp_proc_show, PDE(inode)->data);
549 }
550
551 static const struct file_operations cmtp_proc_fops = {
552         .owner          = THIS_MODULE,
553         .open           = cmtp_proc_open,
554         .read           = seq_read,
555         .llseek         = seq_lseek,
556         .release        = single_release,
557 };
558
559 #else
560
561 static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl)
562 {
563         struct cmtp_session *session = ctrl->driverdata;
564         struct cmtp_application *app;
565         struct list_head *p, *n;
566         int len = 0;
567
568         len += sprintf(page + len, "%s\n\n", cmtp_procinfo(ctrl));
569         len += sprintf(page + len, "addr %s\n", session->name);
570         len += sprintf(page + len, "ctrl %d\n", session->num);
571
572         list_for_each_safe(p, n, &session->applications) {
573                 app = list_entry(p, struct cmtp_application, list);
574                 len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping);
575         }
576
577         if (off + count >= len)
578                 *eof = 1;
579
580         if (len < off)
581                 return 0;
582
583         *start = page + off;
584
585         return ((count < len - off) ? count : len - off);
586 }
587 #endif
588
589 int cmtp_attach_device(struct cmtp_session *session)
590 {
591         unsigned char buf[4];
592         long ret;
593
594         BT_DBG("session %p", session);
595
596         capimsg_setu32(buf, 0, 0);
597
598         cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
599                                 CAPI_FUNCTION_GET_PROFILE, buf, 4);
600
601         ret = wait_event_interruptible_timeout(session->wait,
602                         session->ncontroller, CMTP_INTEROP_TIMEOUT);
603
604         BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
605
606         if (!ret)
607                 return -ETIMEDOUT;
608
609         if (!session->ncontroller)
610                 return -ENODEV;
611
612         if (session->ncontroller > 1)
613                 BT_INFO("Setting up only CAPI controller 1");
614
615         session->ctrl.owner      = THIS_MODULE;
616         session->ctrl.driverdata = session;
617         strcpy(session->ctrl.name, session->name);
618
619         session->ctrl.driver_name   = "cmtp";
620         session->ctrl.load_firmware = cmtp_load_firmware;
621         session->ctrl.reset_ctr     = cmtp_reset_ctr;
622         session->ctrl.register_appl = cmtp_register_appl;
623         session->ctrl.release_appl  = cmtp_release_appl;
624         session->ctrl.send_message  = cmtp_send_message;
625
626         session->ctrl.procinfo      = cmtp_procinfo;
627 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34))
628         session->ctrl.proc_fops = &cmtp_proc_fops;
629 #else
630         session->ctrl.ctr_read_proc = cmtp_ctr_read_proc;
631 #endif
632
633         if (attach_capi_ctr(&session->ctrl) < 0) {
634                 BT_ERR("Can't attach new controller");
635                 return -EBUSY;
636         }
637
638         session->num = session->ctrl.cnr;
639
640         BT_DBG("session %p num %d", session, session->num);
641
642         capimsg_setu32(buf, 0, 1);
643
644         cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
645                                 CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
646
647         cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
648                                 CAPI_FUNCTION_GET_VERSION, buf, 4);
649
650         cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
651                                 CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
652
653         cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
654                                 CAPI_FUNCTION_GET_PROFILE, buf, 4);
655
656         return 0;
657 }
658
659 void cmtp_detach_device(struct cmtp_session *session)
660 {
661         BT_DBG("session %p", session);
662
663         detach_capi_ctr(&session->ctrl);
664 }