OpenCores
URL https://opencores.org/ocsvn/or1k_soc_on_altera_embedded_dev_kit/or1k_soc_on_altera_embedded_dev_kit/trunk

Subversion Repositories or1k_soc_on_altera_embedded_dev_kit

[/] [or1k_soc_on_altera_embedded_dev_kit/] [trunk/] [linux-2.6/] [linux-2.6.24/] [net/] [bluetooth/] [cmtp/] [core.c] - Blame information for rev 3

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 3 xianfeng
/*
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/freezer.h>
33
#include <linux/skbuff.h>
34
#include <linux/socket.h>
35
#include <linux/ioctl.h>
36
#include <linux/file.h>
37
#include <linux/init.h>
38
#include <net/sock.h>
39
 
40
#include <linux/isdn/capilli.h>
41
 
42
#include <net/bluetooth/bluetooth.h>
43
#include <net/bluetooth/l2cap.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 VERSION "1.0"
53
 
54
static DECLARE_RWSEM(cmtp_session_sem);
55
static LIST_HEAD(cmtp_session_list);
56
 
57
static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
58
{
59
        struct cmtp_session *session;
60
        struct list_head *p;
61
 
62
        BT_DBG("");
63
 
64
        list_for_each(p, &cmtp_session_list) {
65
                session = list_entry(p, struct cmtp_session, list);
66
                if (!bacmp(bdaddr, &session->bdaddr))
67
                        return session;
68
        }
69
        return NULL;
70
}
71
 
72
static void __cmtp_link_session(struct cmtp_session *session)
73
{
74
        __module_get(THIS_MODULE);
75
        list_add(&session->list, &cmtp_session_list);
76
}
77
 
78
static void __cmtp_unlink_session(struct cmtp_session *session)
79
{
80
        list_del(&session->list);
81
        module_put(THIS_MODULE);
82
}
83
 
84
static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
85
{
86
        bacpy(&ci->bdaddr, &session->bdaddr);
87
 
88
        ci->flags = session->flags;
89
        ci->state = session->state;
90
 
91
        ci->num = session->num;
92
}
93
 
94
 
95
static inline int cmtp_alloc_block_id(struct cmtp_session *session)
96
{
97
        int i, id = -1;
98
 
99
        for (i = 0; i < 16; i++)
100
                if (!test_and_set_bit(i, &session->blockids)) {
101
                        id = i;
102
                        break;
103
                }
104
 
105
        return id;
106
}
107
 
108
static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
109
{
110
        clear_bit(id, &session->blockids);
111
}
112
 
113
static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
114
{
115
        struct sk_buff *skb = session->reassembly[id], *nskb;
116
        int size;
117
 
118
        BT_DBG("session %p buf %p count %d", session, buf, count);
119
 
120
        size = (skb) ? skb->len + count : count;
121
 
122
        if (!(nskb = alloc_skb(size, GFP_ATOMIC))) {
123
                BT_ERR("Can't allocate memory for CAPI message");
124
                return;
125
        }
126
 
127
        if (skb && (skb->len > 0))
128
                skb_copy_from_linear_data(skb, skb_put(nskb, skb->len), skb->len);
129
 
130
        memcpy(skb_put(nskb, count), buf, count);
131
 
132
        session->reassembly[id] = nskb;
133
 
134
        if (skb)
135
                kfree_skb(skb);
136
}
137
 
138
static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
139
{
140
        __u8 hdr, hdrlen, id;
141
        __u16 len;
142
 
143
        BT_DBG("session %p skb %p len %d", session, skb, skb->len);
144
 
145
        while (skb->len > 0) {
146
                hdr = skb->data[0];
147
 
148
                switch (hdr & 0xc0) {
149
                case 0x40:
150
                        hdrlen = 2;
151
                        len = skb->data[1];
152
                        break;
153
                case 0x80:
154
                        hdrlen = 3;
155
                        len = skb->data[1] | (skb->data[2] << 8);
156
                        break;
157
                default:
158
                        hdrlen = 1;
159
                        len = 0;
160
                        break;
161
                }
162
 
163
                id = (hdr & 0x3c) >> 2;
164
 
165
                BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
166
 
167
                if (hdrlen + len > skb->len) {
168
                        BT_ERR("Wrong size or header information in CMTP frame");
169
                        break;
170
                }
171
 
172
                if (len == 0) {
173
                        skb_pull(skb, hdrlen);
174
                        continue;
175
                }
176
 
177
                switch (hdr & 0x03) {
178
                case 0x00:
179
                        cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
180
                        cmtp_recv_capimsg(session, session->reassembly[id]);
181
                        session->reassembly[id] = NULL;
182
                        break;
183
                case 0x01:
184
                        cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
185
                        break;
186
                default:
187
                        if (session->reassembly[id] != NULL)
188
                                kfree_skb(session->reassembly[id]);
189
                        session->reassembly[id] = NULL;
190
                        break;
191
                }
192
 
193
                skb_pull(skb, hdrlen + len);
194
        }
195
 
196
        kfree_skb(skb);
197
        return 0;
198
}
199
 
200
static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
201
{
202
        struct socket *sock = session->sock;
203
        struct kvec iv = { data, len };
204
        struct msghdr msg;
205
 
206
        BT_DBG("session %p data %p len %d", session, data, len);
207
 
208
        if (!len)
209
                return 0;
210
 
211
        memset(&msg, 0, sizeof(msg));
212
 
213
        return kernel_sendmsg(sock, &msg, &iv, 1, len);
214
}
215
 
216
static void cmtp_process_transmit(struct cmtp_session *session)
217
{
218
        struct sk_buff *skb, *nskb;
219
        unsigned char *hdr;
220
        unsigned int size, tail;
221
 
222
        BT_DBG("session %p", session);
223
 
224
        if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) {
225
                BT_ERR("Can't allocate memory for new frame");
226
                return;
227
        }
228
 
229
        while ((skb = skb_dequeue(&session->transmit))) {
230
                struct cmtp_scb *scb = (void *) skb->cb;
231
 
232
                if ((tail = (session->mtu - nskb->len)) < 5) {
233
                        cmtp_send_frame(session, nskb->data, nskb->len);
234
                        skb_trim(nskb, 0);
235
                        tail = session->mtu;
236
                }
237
 
238
                size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
239
 
240
                if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) {
241
                        skb_queue_head(&session->transmit, skb);
242
                        break;
243
                }
244
 
245
                if (size < 256) {
246
                        hdr = skb_put(nskb, 2);
247
                        hdr[0] = 0x40
248
                                | ((scb->id << 2) & 0x3c)
249
                                | ((skb->len == size) ? 0x00 : 0x01);
250
                        hdr[1] = size;
251
                } else {
252
                        hdr = skb_put(nskb, 3);
253
                        hdr[0] = 0x80
254
                                | ((scb->id << 2) & 0x3c)
255
                                | ((skb->len == size) ? 0x00 : 0x01);
256
                        hdr[1] = size & 0xff;
257
                        hdr[2] = size >> 8;
258
                }
259
 
260
                skb_copy_from_linear_data(skb, skb_put(nskb, size), size);
261
                skb_pull(skb, size);
262
 
263
                if (skb->len > 0) {
264
                        skb_queue_head(&session->transmit, skb);
265
                } else {
266
                        cmtp_free_block_id(session, scb->id);
267
                        if (scb->data) {
268
                                cmtp_send_frame(session, nskb->data, nskb->len);
269
                                skb_trim(nskb, 0);
270
                        }
271
                        kfree_skb(skb);
272
                }
273
        }
274
 
275
        cmtp_send_frame(session, nskb->data, nskb->len);
276
 
277
        kfree_skb(nskb);
278
}
279
 
280
static int cmtp_session(void *arg)
281
{
282
        struct cmtp_session *session = arg;
283
        struct sock *sk = session->sock->sk;
284
        struct sk_buff *skb;
285
        wait_queue_t wait;
286
 
287
        BT_DBG("session %p", session);
288
 
289
        daemonize("kcmtpd_ctr_%d", session->num);
290
        set_user_nice(current, -15);
291
 
292
        init_waitqueue_entry(&wait, current);
293
        add_wait_queue(sk->sk_sleep, &wait);
294
        while (!atomic_read(&session->terminate)) {
295
                set_current_state(TASK_INTERRUPTIBLE);
296
 
297
                if (sk->sk_state != BT_CONNECTED)
298
                        break;
299
 
300
                while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
301
                        skb_orphan(skb);
302
                        cmtp_recv_frame(session, skb);
303
                }
304
 
305
                cmtp_process_transmit(session);
306
 
307
                schedule();
308
        }
309
        set_current_state(TASK_RUNNING);
310
        remove_wait_queue(sk->sk_sleep, &wait);
311
 
312
        down_write(&cmtp_session_sem);
313
 
314
        if (!(session->flags & (1 << CMTP_LOOPBACK)))
315
                cmtp_detach_device(session);
316
 
317
        fput(session->sock->file);
318
 
319
        __cmtp_unlink_session(session);
320
 
321
        up_write(&cmtp_session_sem);
322
 
323
        kfree(session);
324
        return 0;
325
}
326
 
327
int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
328
{
329
        struct cmtp_session *session, *s;
330
        bdaddr_t src, dst;
331
        int i, err;
332
 
333
        BT_DBG("");
334
 
335
        baswap(&src, &bt_sk(sock->sk)->src);
336
        baswap(&dst, &bt_sk(sock->sk)->dst);
337
 
338
        session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL);
339
        if (!session)
340
                return -ENOMEM;
341
 
342
        down_write(&cmtp_session_sem);
343
 
344
        s = __cmtp_get_session(&bt_sk(sock->sk)->dst);
345
        if (s && s->state == BT_CONNECTED) {
346
                err = -EEXIST;
347
                goto failed;
348
        }
349
 
350
        bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst);
351
 
352
        session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu);
353
 
354
        BT_DBG("mtu %d", session->mtu);
355
 
356
        sprintf(session->name, "%s", batostr(&dst));
357
 
358
        session->sock  = sock;
359
        session->state = BT_CONFIG;
360
 
361
        init_waitqueue_head(&session->wait);
362
 
363
        session->msgnum = CMTP_INITIAL_MSGNUM;
364
 
365
        INIT_LIST_HEAD(&session->applications);
366
 
367
        skb_queue_head_init(&session->transmit);
368
 
369
        for (i = 0; i < 16; i++)
370
                session->reassembly[i] = NULL;
371
 
372
        session->flags = req->flags;
373
 
374
        __cmtp_link_session(session);
375
 
376
        err = kernel_thread(cmtp_session, session, CLONE_KERNEL);
377
        if (err < 0)
378
                goto unlink;
379
 
380
        if (!(session->flags & (1 << CMTP_LOOPBACK))) {
381
                err = cmtp_attach_device(session);
382
                if (err < 0)
383
                        goto detach;
384
        }
385
 
386
        up_write(&cmtp_session_sem);
387
        return 0;
388
 
389
detach:
390
        cmtp_detach_device(session);
391
 
392
unlink:
393
        __cmtp_unlink_session(session);
394
 
395
failed:
396
        up_write(&cmtp_session_sem);
397
        kfree(session);
398
        return err;
399
}
400
 
401
int cmtp_del_connection(struct cmtp_conndel_req *req)
402
{
403
        struct cmtp_session *session;
404
        int err = 0;
405
 
406
        BT_DBG("");
407
 
408
        down_read(&cmtp_session_sem);
409
 
410
        session = __cmtp_get_session(&req->bdaddr);
411
        if (session) {
412
                /* Flush the transmit queue */
413
                skb_queue_purge(&session->transmit);
414
 
415
                /* Kill session thread */
416
                atomic_inc(&session->terminate);
417
                cmtp_schedule(session);
418
        } else
419
                err = -ENOENT;
420
 
421
        up_read(&cmtp_session_sem);
422
        return err;
423
}
424
 
425
int cmtp_get_connlist(struct cmtp_connlist_req *req)
426
{
427
        struct list_head *p;
428
        int err = 0, n = 0;
429
 
430
        BT_DBG("");
431
 
432
        down_read(&cmtp_session_sem);
433
 
434
        list_for_each(p, &cmtp_session_list) {
435
                struct cmtp_session *session;
436
                struct cmtp_conninfo ci;
437
 
438
                session = list_entry(p, struct cmtp_session, list);
439
 
440
                __cmtp_copy_session(session, &ci);
441
 
442
                if (copy_to_user(req->ci, &ci, sizeof(ci))) {
443
                        err = -EFAULT;
444
                        break;
445
                }
446
 
447
                if (++n >= req->cnum)
448
                        break;
449
 
450
                req->ci++;
451
        }
452
        req->cnum = n;
453
 
454
        up_read(&cmtp_session_sem);
455
        return err;
456
}
457
 
458
int cmtp_get_conninfo(struct cmtp_conninfo *ci)
459
{
460
        struct cmtp_session *session;
461
        int err = 0;
462
 
463
        down_read(&cmtp_session_sem);
464
 
465
        session = __cmtp_get_session(&ci->bdaddr);
466
        if (session)
467
                __cmtp_copy_session(session, ci);
468
        else
469
                err = -ENOENT;
470
 
471
        up_read(&cmtp_session_sem);
472
        return err;
473
}
474
 
475
 
476
static int __init cmtp_init(void)
477
{
478
        l2cap_load();
479
 
480
        BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION);
481
 
482
        cmtp_init_sockets();
483
 
484
        return 0;
485
}
486
 
487
static void __exit cmtp_exit(void)
488
{
489
        cmtp_cleanup_sockets();
490
}
491
 
492
module_init(cmtp_init);
493
module_exit(cmtp_exit);
494
 
495
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
496
MODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION);
497
MODULE_VERSION(VERSION);
498
MODULE_LICENSE("GPL");
499
MODULE_ALIAS("bt-proto-5");

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.