blob: 46ac686c8911e70d46341d046c56ff896d554ccf [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
Syam Sidhardhan2ad8f542012-10-23 19:02:18 +053023#include <linux/export.h>
Alexey Dobriyan9a58a802010-01-14 03:10:54 -080024#include <linux/proc_fs.h>
25#include <linux/seq_file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include <linux/types.h>
27#include <linux/errno.h>
28#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#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>
Szymon Jancfada4ac2011-03-21 14:20:06 +010038#include <linux/kthread.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#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
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#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{
Marcel Holtmann25ea6db2006-07-06 15:40:09 +020075 struct cmtp_application *app = kzalloc(sizeof(*app), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
77 BT_DBG("session %p application %p appl %d", session, app, appl);
78
79 if (!app)
80 return NULL;
81
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103
Geliang Tang7eb74042015-12-18 23:33:25 +0800104 list_for_each_entry(app, &session->applications, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 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
Szymon Jancfada4ac2011-03-21 14:20:06 +0100145 wake_up_interruptible(sk_sleep(session->sock->sk));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146}
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
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200157 skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC);
158 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 BT_ERR("Can't allocate memory for interoperability packet");
160 return;
161 }
162
163 s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
164
165 capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
166 capimsg_setu16(s, 2, appl);
167 capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
168 capimsg_setu8 (s, 5, subcmd);
169 capimsg_setu16(s, 6, msgnum);
170
171 /* Interoperability selector (Bluetooth Device Management) */
172 capimsg_setu16(s, 8, 0x0001);
173
174 capimsg_setu8 (s, 10, 3 + len);
175 capimsg_setu16(s, 11, function);
176 capimsg_setu8 (s, 13, len);
177
178 if (len > 0)
179 memcpy(s + 14, buf, len);
180
181 cmtp_send_capimsg(session, skb);
182}
183
184static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
185{
186 struct capi_ctr *ctrl = &session->ctrl;
187 struct cmtp_application *application;
188 __u16 appl, msgnum, func, info;
189 __u32 controller;
190
191 BT_DBG("session %p skb %p len %d", session, skb, skb->len);
192
193 switch (CAPIMSG_SUBCOMMAND(skb->data)) {
194 case CAPI_CONF:
Marcel Holtmannf4777562007-01-08 02:16:23 +0100195 if (skb->len < CAPI_MSG_BASELEN + 10)
196 break;
197
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
199 info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
200
201 switch (func) {
202 case CAPI_FUNCTION_REGISTER:
203 msgnum = CAPIMSG_MSGID(skb->data);
204
205 application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
206 if (application) {
207 application->state = BT_CONNECTED;
208 application->msgnum = 0;
209 application->mapping = CAPIMSG_APPID(skb->data);
210 wake_up_interruptible(&session->wait);
211 }
212
213 break;
214
215 case CAPI_FUNCTION_RELEASE:
216 appl = CAPIMSG_APPID(skb->data);
217
218 application = cmtp_application_get(session, CMTP_MAPPING, appl);
219 if (application) {
220 application->state = BT_CLOSED;
221 application->msgnum = 0;
222 wake_up_interruptible(&session->wait);
223 }
224
225 break;
226
227 case CAPI_FUNCTION_GET_PROFILE:
Marcel Holtmannf4777562007-01-08 02:16:23 +0100228 if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile))
229 break;
230
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
232 msgnum = CAPIMSG_MSGID(skb->data);
233
234 if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
235 session->ncontroller = controller;
236 wake_up_interruptible(&session->wait);
237 break;
238 }
239
240 if (!info && ctrl) {
241 memcpy(&ctrl->profile,
242 skb->data + CAPI_MSG_BASELEN + 11,
243 sizeof(capi_profile));
244 session->state = BT_CONNECTED;
245 capi_ctr_ready(ctrl);
246 }
247
248 break;
249
250 case CAPI_FUNCTION_GET_MANUFACTURER:
Marcel Holtmannf4777562007-01-08 02:16:23 +0100251 if (skb->len < CAPI_MSG_BASELEN + 15)
252 break;
253
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 if (!info && ctrl) {
Marcel Holtmannf4777562007-01-08 02:16:23 +0100255 int len = min_t(uint, CAPI_MANUFACTURER_LEN,
256 skb->data[CAPI_MSG_BASELEN + 14]);
257
258 memset(ctrl->manu, 0, CAPI_MANUFACTURER_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 strncpy(ctrl->manu,
Marcel Holtmannf4777562007-01-08 02:16:23 +0100260 skb->data + CAPI_MSG_BASELEN + 15, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 }
262
263 break;
264
265 case CAPI_FUNCTION_GET_VERSION:
Marcel Holtmannf4777562007-01-08 02:16:23 +0100266 if (skb->len < CAPI_MSG_BASELEN + 32)
267 break;
268
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 if (!info && ctrl) {
270 ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
271 ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
272 ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
273 ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
274 }
275
276 break;
277
278 case CAPI_FUNCTION_GET_SERIAL_NUMBER:
Marcel Holtmannf4777562007-01-08 02:16:23 +0100279 if (skb->len < CAPI_MSG_BASELEN + 17)
280 break;
281
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 if (!info && ctrl) {
Marcel Holtmannf4777562007-01-08 02:16:23 +0100283 int len = min_t(uint, CAPI_SERIAL_LEN,
284 skb->data[CAPI_MSG_BASELEN + 16]);
285
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
287 strncpy(ctrl->serial,
Marcel Holtmannf4777562007-01-08 02:16:23 +0100288 skb->data + CAPI_MSG_BASELEN + 17, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 }
290
291 break;
292 }
293
294 break;
295
296 case CAPI_IND:
Marcel Holtmannf4777562007-01-08 02:16:23 +0100297 if (skb->len < CAPI_MSG_BASELEN + 6)
298 break;
299
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
301
302 if (func == CAPI_FUNCTION_LOOPBACK) {
Marcel Holtmannf4777562007-01-08 02:16:23 +0100303 int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6,
304 skb->data[CAPI_MSG_BASELEN + 5]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 appl = CAPIMSG_APPID(skb->data);
306 msgnum = CAPIMSG_MSGID(skb->data);
307 cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
Marcel Holtmannf4777562007-01-08 02:16:23 +0100308 skb->data + CAPI_MSG_BASELEN + 6, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 }
310
311 break;
312 }
313
314 kfree_skb(skb);
315}
316
317void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
318{
319 struct capi_ctr *ctrl = &session->ctrl;
320 struct cmtp_application *application;
David Millerd29d04c2011-05-19 17:50:05 -0400321 __u16 appl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 __u32 contr;
323
324 BT_DBG("session %p skb %p len %d", session, skb, skb->len);
325
Marcel Holtmannf4777562007-01-08 02:16:23 +0100326 if (skb->len < CAPI_MSG_BASELEN)
327 return;
328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
330 cmtp_recv_interopmsg(session, skb);
331 return;
332 }
333
Marcel Holtmannb2ddeb12015-04-03 11:02:09 -0700334 if (session->flags & BIT(CMTP_LOOPBACK)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 kfree_skb(skb);
336 return;
337 }
338
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 appl = CAPIMSG_APPID(skb->data);
340 contr = CAPIMSG_CONTROL(skb->data);
341
342 application = cmtp_application_get(session, CMTP_MAPPING, appl);
343 if (application) {
344 appl = application->appl;
345 CAPIMSG_SETAPPID(skb->data, appl);
346 } else {
347 BT_ERR("Can't find application with id %d", appl);
348 kfree_skb(skb);
349 return;
350 }
351
352 if ((contr & 0x7f) == 0x01) {
353 contr = (contr & 0xffffff80) | session->num;
354 CAPIMSG_SETCONTROL(skb->data, contr);
355 }
356
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 capi_ctr_handle_message(ctrl, appl, skb);
358}
359
360static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
361{
362 BT_DBG("ctrl %p data %p", ctrl, data);
363
364 return 0;
365}
366
367static void cmtp_reset_ctr(struct capi_ctr *ctrl)
368{
369 struct cmtp_session *session = ctrl->driverdata;
370
371 BT_DBG("ctrl %p", ctrl);
372
Tilman Schmidt4e329972009-06-07 09:09:23 +0000373 capi_ctr_down(ctrl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
Peter Hurley71765222011-08-05 10:44:21 -0400375 atomic_inc(&session->terminate);
376 wake_up_process(session->task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377}
378
379static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
380{
381 DECLARE_WAITQUEUE(wait, current);
382 struct cmtp_session *session = ctrl->driverdata;
383 struct cmtp_application *application;
384 unsigned long timeo = CMTP_INTEROP_TIMEOUT;
385 unsigned char buf[8];
386 int err = 0, nconn, want = rp->level3cnt;
387
388 BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
389 ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
390
391 application = cmtp_application_add(session, appl);
392 if (!application) {
393 BT_ERR("Can't allocate memory for new application");
394 return;
395 }
396
397 if (want < 0)
398 nconn = ctrl->profile.nbchannel * -want;
399 else
400 nconn = want;
401
402 if (nconn == 0)
403 nconn = ctrl->profile.nbchannel;
404
405 capimsg_setu16(buf, 0, nconn);
406 capimsg_setu16(buf, 2, rp->datablkcnt);
407 capimsg_setu16(buf, 4, rp->datablklen);
408
409 application->state = BT_CONFIG;
410 application->msgnum = cmtp_msgnum_get(session);
411
412 cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
413 CAPI_FUNCTION_REGISTER, buf, 6);
414
415 add_wait_queue(&session->wait, &wait);
416 while (1) {
417 set_current_state(TASK_INTERRUPTIBLE);
418
419 if (!timeo) {
420 err = -EAGAIN;
421 break;
422 }
423
424 if (application->state == BT_CLOSED) {
425 err = -application->err;
426 break;
427 }
428
429 if (application->state == BT_CONNECTED)
430 break;
431
432 if (signal_pending(current)) {
433 err = -EINTR;
434 break;
435 }
436
437 timeo = schedule_timeout(timeo);
438 }
439 set_current_state(TASK_RUNNING);
440 remove_wait_queue(&session->wait, &wait);
441
442 if (err) {
443 cmtp_application_del(session, application);
444 return;
445 }
446}
447
448static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
449{
450 struct cmtp_session *session = ctrl->driverdata;
451 struct cmtp_application *application;
452
453 BT_DBG("ctrl %p appl %d", ctrl, appl);
454
455 application = cmtp_application_get(session, CMTP_APPLID, appl);
456 if (!application) {
457 BT_ERR("Can't find application");
458 return;
459 }
460
461 application->msgnum = cmtp_msgnum_get(session);
462
463 cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
464 CAPI_FUNCTION_RELEASE, NULL, 0);
465
466 wait_event_interruptible_timeout(session->wait,
467 (application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT);
468
469 cmtp_application_del(session, application);
470}
471
472static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
473{
474 struct cmtp_session *session = ctrl->driverdata;
475 struct cmtp_application *application;
476 __u16 appl;
477 __u32 contr;
478
479 BT_DBG("ctrl %p skb %p", ctrl, skb);
480
481 appl = CAPIMSG_APPID(skb->data);
482 contr = CAPIMSG_CONTROL(skb->data);
483
484 application = cmtp_application_get(session, CMTP_APPLID, appl);
485 if ((!application) || (application->state != BT_CONNECTED)) {
486 BT_ERR("Can't find application with id %d", appl);
487 return CAPI_ILLAPPNR;
488 }
489
490 CAPIMSG_SETAPPID(skb->data, application->mapping);
491
492 if ((contr & 0x7f) == session->num) {
493 contr = (contr & 0xffffff80) | 0x01;
494 CAPIMSG_SETCONTROL(skb->data, contr);
495 }
496
497 cmtp_send_capimsg(session, skb);
498
499 return CAPI_NOERROR;
500}
501
502static char *cmtp_procinfo(struct capi_ctr *ctrl)
503{
504 return "CAPI Message Transport Protocol";
505}
506
Alexey Dobriyan9a58a802010-01-14 03:10:54 -0800507static int cmtp_proc_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508{
Alexey Dobriyan9a58a802010-01-14 03:10:54 -0800509 struct capi_ctr *ctrl = m->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 struct cmtp_session *session = ctrl->driverdata;
511 struct cmtp_application *app;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
Alexey Dobriyan9a58a802010-01-14 03:10:54 -0800513 seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl));
514 seq_printf(m, "addr %s\n", session->name);
515 seq_printf(m, "ctrl %d\n", session->num);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
Geliang Tang7eb74042015-12-18 23:33:25 +0800517 list_for_each_entry(app, &session->applications, list) {
Alexey Dobriyan9a58a802010-01-14 03:10:54 -0800518 seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 }
520
Alexey Dobriyan9a58a802010-01-14 03:10:54 -0800521 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522}
523
Alexey Dobriyan9a58a802010-01-14 03:10:54 -0800524static int cmtp_proc_open(struct inode *inode, struct file *file)
525{
Al Virod9dda782013-03-31 18:16:14 -0400526 return single_open(file, cmtp_proc_show, PDE_DATA(inode));
Alexey Dobriyan9a58a802010-01-14 03:10:54 -0800527}
528
529static const struct file_operations cmtp_proc_fops = {
530 .owner = THIS_MODULE,
531 .open = cmtp_proc_open,
532 .read = seq_read,
533 .llseek = seq_lseek,
534 .release = single_release,
535};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
537int cmtp_attach_device(struct cmtp_session *session)
538{
539 unsigned char buf[4];
540 long ret;
541
542 BT_DBG("session %p", session);
543
544 capimsg_setu32(buf, 0, 0);
545
546 cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
547 CAPI_FUNCTION_GET_PROFILE, buf, 4);
548
549 ret = wait_event_interruptible_timeout(session->wait,
550 session->ncontroller, CMTP_INTEROP_TIMEOUT);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900551
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
553
554 if (!ret)
555 return -ETIMEDOUT;
556
557 if (!session->ncontroller)
558 return -ENODEV;
559
560 if (session->ncontroller > 1)
561 BT_INFO("Setting up only CAPI controller 1");
562
563 session->ctrl.owner = THIS_MODULE;
564 session->ctrl.driverdata = session;
565 strcpy(session->ctrl.name, session->name);
566
567 session->ctrl.driver_name = "cmtp";
568 session->ctrl.load_firmware = cmtp_load_firmware;
569 session->ctrl.reset_ctr = cmtp_reset_ctr;
570 session->ctrl.register_appl = cmtp_register_appl;
571 session->ctrl.release_appl = cmtp_release_appl;
572 session->ctrl.send_message = cmtp_send_message;
573
574 session->ctrl.procinfo = cmtp_procinfo;
Alexey Dobriyan9a58a802010-01-14 03:10:54 -0800575 session->ctrl.proc_fops = &cmtp_proc_fops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
577 if (attach_capi_ctr(&session->ctrl) < 0) {
578 BT_ERR("Can't attach new controller");
579 return -EBUSY;
580 }
581
582 session->num = session->ctrl.cnr;
583
584 BT_DBG("session %p num %d", session, session->num);
585
586 capimsg_setu32(buf, 0, 1);
587
588 cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
589 CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
590
591 cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
592 CAPI_FUNCTION_GET_VERSION, buf, 4);
593
594 cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
595 CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
596
597 cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
598 CAPI_FUNCTION_GET_PROFILE, buf, 4);
599
600 return 0;
601}
602
603void cmtp_detach_device(struct cmtp_session *session)
604{
605 BT_DBG("session %p", session);
606
607 detach_capi_ctr(&session->ctrl);
608}