• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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 
45 #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 
cmtp_application_add(struct cmtp_session * session,__u16 appl)71 static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
72 {
73 	struct cmtp_application *app = kzalloc(sizeof(*app), GFP_KERNEL);
74 
75 	BT_DBG("session %p application %p appl %d", session, app, appl);
76 
77 	if (!app)
78 		return NULL;
79 
80 	app->state = BT_OPEN;
81 	app->appl = appl;
82 
83 	list_add_tail(&app->list, &session->applications);
84 
85 	return app;
86 }
87 
cmtp_application_del(struct cmtp_session * session,struct cmtp_application * app)88 static 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 
cmtp_application_get(struct cmtp_session * session,int pattern,__u16 value)98 static 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 
cmtp_msgnum_get(struct cmtp_session * session)124 static 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 
cmtp_send_capimsg(struct cmtp_session * session,struct sk_buff * skb)134 static 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 
cmtp_send_interopmsg(struct cmtp_session * session,__u8 subcmd,__u16 appl,__u16 msgnum,__u16 function,unsigned char * buf,int len)148 static 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 
cmtp_recv_interopmsg(struct cmtp_session * session,struct sk_buff * skb)183 static 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:
194 		if (skb->len < CAPI_MSG_BASELEN + 10)
195 			break;
196 
197 		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:
227 			if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile))
228 				break;
229 
230 			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:
250 			if (skb->len < CAPI_MSG_BASELEN + 15)
251 				break;
252 
253 			controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
254 
255 			if (!info && ctrl) {
256 				int len = min_t(uint, CAPI_MANUFACTURER_LEN,
257 						skb->data[CAPI_MSG_BASELEN + 14]);
258 
259 				memset(ctrl->manu, 0, CAPI_MANUFACTURER_LEN);
260 				strncpy(ctrl->manu,
261 					skb->data + CAPI_MSG_BASELEN + 15, len);
262 			}
263 
264 			break;
265 
266 		case CAPI_FUNCTION_GET_VERSION:
267 			if (skb->len < CAPI_MSG_BASELEN + 32)
268 				break;
269 
270 			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:
282 			if (skb->len < CAPI_MSG_BASELEN + 17)
283 				break;
284 
285 			controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
286 
287 			if (!info && ctrl) {
288 				int len = min_t(uint, CAPI_SERIAL_LEN,
289 						skb->data[CAPI_MSG_BASELEN + 16]);
290 
291 				memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
292 				strncpy(ctrl->serial,
293 					skb->data + CAPI_MSG_BASELEN + 17, len);
294 			}
295 
296 			break;
297 		}
298 
299 		break;
300 
301 	case CAPI_IND:
302 		if (skb->len < CAPI_MSG_BASELEN + 6)
303 			break;
304 
305 		func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
306 
307 		if (func == CAPI_FUNCTION_LOOPBACK) {
308 			int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6,
309 						skb->data[CAPI_MSG_BASELEN + 5]);
310 			appl = CAPIMSG_APPID(skb->data);
311 			msgnum = CAPIMSG_MSGID(skb->data);
312 			cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
313 						skb->data + CAPI_MSG_BASELEN + 6, len);
314 		}
315 
316 		break;
317 	}
318 
319 	kfree_skb(skb);
320 }
321 
cmtp_recv_capimsg(struct cmtp_session * session,struct sk_buff * skb)322 void 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 
331 	if (skb->len < CAPI_MSG_BASELEN)
332 		return;
333 
334 	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 
cmtp_load_firmware(struct capi_ctr * ctrl,capiloaddata * data)372 static 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 
cmtp_reset_ctr(struct capi_ctr * ctrl)379 static void cmtp_reset_ctr(struct capi_ctr *ctrl)
380 {
381 	struct cmtp_session *session = ctrl->driverdata;
382 
383 	BT_DBG("ctrl %p", ctrl);
384 
385 	capi_ctr_reseted(ctrl);
386 
387 	atomic_inc(&session->terminate);
388 	cmtp_schedule(session);
389 }
390 
cmtp_register_appl(struct capi_ctr * ctrl,__u16 appl,capi_register_params * rp)391 static 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 
cmtp_release_appl(struct capi_ctr * ctrl,__u16 appl)460 static 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 
cmtp_send_message(struct capi_ctr * ctrl,struct sk_buff * skb)484 static 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 
cmtp_procinfo(struct capi_ctr * ctrl)514 static char *cmtp_procinfo(struct capi_ctr *ctrl)
515 {
516 	return "CAPI Message Transport Protocol";
517 }
518 
cmtp_ctr_read_proc(char * page,char ** start,off_t off,int count,int * eof,struct capi_ctr * ctrl)519 static 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 
cmtp_attach_device(struct cmtp_session * session)547 int 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);
561 
562 	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 
cmtp_detach_device(struct cmtp_session * session)613 void cmtp_detach_device(struct cmtp_session *session)
614 {
615 	BT_DBG("session %p", session);
616 
617 	detach_capi_ctr(&session->ctrl);
618 }
619