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