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