blob: 612955679b347cbfeb53707999abe7fc69963999 [file] [log] [blame]
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001/*
2 BlueZ - Bluetooth protocol stack for Linux
3 Copyright (C) 2000-2001 Qualcomm Incorporated
4 Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org>
5 Copyright (C) 2010 Google Inc.
6
7 Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation;
12
13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
16 IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
17 CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
18 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21
22 ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
23 COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
24 SOFTWARE IS DISCLAIMED.
25*/
26
27/* Bluetooth L2CAP sockets. */
28
29#include <net/bluetooth/bluetooth.h>
Gustavo F. Padovan33575df2011-02-04 02:48:48 -020030#include <net/bluetooth/hci_core.h>
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -020031#include <net/bluetooth/l2cap.h>
32
Gustavo F. Padovan6ddc0482011-02-04 03:23:31 -020033/* ---- L2CAP timers ---- */
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -020034static void l2cap_sock_timeout(unsigned long arg)
35{
36 struct sock *sk = (struct sock *) arg;
37 int reason;
38
39 BT_DBG("sock %p state %d", sk, sk->sk_state);
40
41 bh_lock_sock(sk);
42
43 if (sock_owned_by_user(sk)) {
44 /* sk is owned by user. Try again later */
45 l2cap_sock_set_timer(sk, HZ / 5);
46 bh_unlock_sock(sk);
47 sock_put(sk);
48 return;
49 }
50
51 if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)
52 reason = ECONNREFUSED;
53 else if (sk->sk_state == BT_CONNECT &&
Gustavo F. Padovan43434782011-04-12 18:31:57 -030054 l2cap_pi(sk)->chan->sec_level != BT_SECURITY_SDP)
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -020055 reason = ECONNREFUSED;
56 else
57 reason = ETIMEDOUT;
58
59 __l2cap_sock_close(sk, reason);
60
61 bh_unlock_sock(sk);
62
63 l2cap_sock_kill(sk);
64 sock_put(sk);
65}
66
Gustavo F. Padovan6ddc0482011-02-04 03:23:31 -020067void l2cap_sock_set_timer(struct sock *sk, long timeout)
68{
69 BT_DBG("sk %p state %d timeout %ld", sk, sk->sk_state, timeout);
70 sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout);
71}
72
73void l2cap_sock_clear_timer(struct sock *sk)
74{
75 BT_DBG("sock %p state %d", sk, sk->sk_state);
76 sk_stop_timer(sk, &sk->sk_timer);
77}
78
Gustavo F. Padovanaf6bcd82011-02-04 02:40:28 -020079static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src)
80{
81 struct sock *sk;
82 struct hlist_node *node;
83 sk_for_each(sk, node, &l2cap_sk_list.head)
84 if (l2cap_pi(sk)->sport == psm && !bacmp(&bt_sk(sk)->src, src))
85 goto found;
86 sk = NULL;
87found:
88 return sk;
89}
90
91static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
92{
93 struct sock *sk = sock->sk;
Gustavo F. Padovan43434782011-04-12 18:31:57 -030094 struct l2cap_chan *chan = l2cap_pi(sk)->chan;
Gustavo F. Padovanaf6bcd82011-02-04 02:40:28 -020095 struct sockaddr_l2 la;
96 int len, err = 0;
97
98 BT_DBG("sk %p", sk);
99
100 if (!addr || addr->sa_family != AF_BLUETOOTH)
101 return -EINVAL;
102
103 memset(&la, 0, sizeof(la));
104 len = min_t(unsigned int, sizeof(la), alen);
105 memcpy(&la, addr, len);
106
Ville Tervob62f3282011-02-10 22:38:50 -0300107 if (la.l2_cid && la.l2_psm)
Gustavo F. Padovanaf6bcd82011-02-04 02:40:28 -0200108 return -EINVAL;
109
110 lock_sock(sk);
111
112 if (sk->sk_state != BT_OPEN) {
113 err = -EBADFD;
114 goto done;
115 }
116
117 if (la.l2_psm) {
118 __u16 psm = __le16_to_cpu(la.l2_psm);
119
120 /* PSM must be odd and lsb of upper byte must be 0 */
121 if ((psm & 0x0101) != 0x0001) {
122 err = -EINVAL;
123 goto done;
124 }
125
126 /* Restrict usage of well-known PSMs */
127 if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) {
128 err = -EACCES;
129 goto done;
130 }
131 }
132
133 write_lock_bh(&l2cap_sk_list.lock);
134
135 if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) {
136 err = -EADDRINUSE;
137 } else {
138 /* Save source address */
139 bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
140 l2cap_pi(sk)->psm = la.l2_psm;
141 l2cap_pi(sk)->sport = la.l2_psm;
142 sk->sk_state = BT_BOUND;
143
144 if (__le16_to_cpu(la.l2_psm) == 0x0001 ||
145 __le16_to_cpu(la.l2_psm) == 0x0003)
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300146 chan->sec_level = BT_SECURITY_SDP;
Gustavo F. Padovanaf6bcd82011-02-04 02:40:28 -0200147 }
148
Ville Tervob62f3282011-02-10 22:38:50 -0300149 if (la.l2_cid)
150 l2cap_pi(sk)->scid = la.l2_cid;
151
Gustavo F. Padovanaf6bcd82011-02-04 02:40:28 -0200152 write_unlock_bh(&l2cap_sk_list.lock);
153
154done:
155 release_sock(sk);
156 return err;
157}
158
Gustavo F. Padovan4e34c502011-02-04 02:56:13 -0200159static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
160{
161 struct sock *sk = sock->sk;
162 struct sockaddr_l2 la;
163 int len, err = 0;
164
165 BT_DBG("sk %p", sk);
166
167 if (!addr || alen < sizeof(addr->sa_family) ||
168 addr->sa_family != AF_BLUETOOTH)
169 return -EINVAL;
170
171 memset(&la, 0, sizeof(la));
172 len = min_t(unsigned int, sizeof(la), alen);
173 memcpy(&la, addr, len);
174
Ville Tervoacd7d372011-02-10 22:38:49 -0300175 if (la.l2_cid && la.l2_psm)
Gustavo F. Padovan4e34c502011-02-04 02:56:13 -0200176 return -EINVAL;
177
178 lock_sock(sk);
179
180 if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM)
Ville Tervoacd7d372011-02-10 22:38:49 -0300181 && !(la.l2_psm || la.l2_cid)) {
Gustavo F. Padovan4e34c502011-02-04 02:56:13 -0200182 err = -EINVAL;
183 goto done;
184 }
185
186 switch (l2cap_pi(sk)->mode) {
187 case L2CAP_MODE_BASIC:
188 break;
189 case L2CAP_MODE_ERTM:
190 case L2CAP_MODE_STREAMING:
191 if (!disable_ertm)
192 break;
193 /* fall through */
194 default:
195 err = -ENOTSUPP;
196 goto done;
197 }
198
199 switch (sk->sk_state) {
200 case BT_CONNECT:
201 case BT_CONNECT2:
202 case BT_CONFIG:
203 /* Already connecting */
204 goto wait;
205
206 case BT_CONNECTED:
207 /* Already connected */
208 err = -EISCONN;
209 goto done;
210
211 case BT_OPEN:
212 case BT_BOUND:
213 /* Can connect */
214 break;
215
216 default:
217 err = -EBADFD;
218 goto done;
219 }
220
221 /* PSM must be odd and lsb of upper byte must be 0 */
222 if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 &&
Ville Tervoacd7d372011-02-10 22:38:49 -0300223 sk->sk_type != SOCK_RAW && !la.l2_cid) {
Gustavo F. Padovan4e34c502011-02-04 02:56:13 -0200224 err = -EINVAL;
225 goto done;
226 }
227
228 /* Set destination address and psm */
229 bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
230 l2cap_pi(sk)->psm = la.l2_psm;
Ville Tervoacd7d372011-02-10 22:38:49 -0300231 l2cap_pi(sk)->dcid = la.l2_cid;
Gustavo F. Padovan4e34c502011-02-04 02:56:13 -0200232
Gustavo F. Padovan77a74c72011-04-12 18:17:14 -0300233 err = l2cap_chan_connect(l2cap_pi(sk)->chan);
Gustavo F. Padovan4e34c502011-02-04 02:56:13 -0200234 if (err)
235 goto done;
236
237wait:
238 err = bt_sock_wait_state(sk, BT_CONNECTED,
239 sock_sndtimeo(sk, flags & O_NONBLOCK));
240done:
241 release_sock(sk);
242 return err;
243}
244
Gustavo F. Padovanaf6bcd82011-02-04 02:40:28 -0200245static int l2cap_sock_listen(struct socket *sock, int backlog)
246{
247 struct sock *sk = sock->sk;
248 int err = 0;
249
250 BT_DBG("sk %p backlog %d", sk, backlog);
251
252 lock_sock(sk);
253
254 if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM)
255 || sk->sk_state != BT_BOUND) {
256 err = -EBADFD;
257 goto done;
258 }
259
260 switch (l2cap_pi(sk)->mode) {
261 case L2CAP_MODE_BASIC:
262 break;
263 case L2CAP_MODE_ERTM:
264 case L2CAP_MODE_STREAMING:
265 if (!disable_ertm)
266 break;
267 /* fall through */
268 default:
269 err = -ENOTSUPP;
270 goto done;
271 }
272
Gustavo F. Padovancd69a032011-04-05 15:24:40 -0300273 if (!l2cap_pi(sk)->psm && !l2cap_pi(sk)->scid) {
Gustavo F. Padovanaf6bcd82011-02-04 02:40:28 -0200274 bdaddr_t *src = &bt_sk(sk)->src;
275 u16 psm;
276
277 err = -EINVAL;
278
279 write_lock_bh(&l2cap_sk_list.lock);
280
281 for (psm = 0x1001; psm < 0x1100; psm += 2)
282 if (!__l2cap_get_sock_by_addr(cpu_to_le16(psm), src)) {
283 l2cap_pi(sk)->psm = cpu_to_le16(psm);
284 l2cap_pi(sk)->sport = cpu_to_le16(psm);
285 err = 0;
286 break;
287 }
288
289 write_unlock_bh(&l2cap_sk_list.lock);
290
291 if (err < 0)
292 goto done;
293 }
294
295 sk->sk_max_ack_backlog = backlog;
296 sk->sk_ack_backlog = 0;
297 sk->sk_state = BT_LISTEN;
298
299done:
300 release_sock(sk);
301 return err;
302}
303
Gustavo F. Padovanc47b7c72011-02-04 02:42:23 -0200304static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags)
305{
306 DECLARE_WAITQUEUE(wait, current);
307 struct sock *sk = sock->sk, *nsk;
308 long timeo;
309 int err = 0;
310
311 lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
312
313 if (sk->sk_state != BT_LISTEN) {
314 err = -EBADFD;
315 goto done;
316 }
317
318 timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
319
320 BT_DBG("sk %p timeo %ld", sk, timeo);
321
322 /* Wait for an incoming connection. (wake-one). */
323 add_wait_queue_exclusive(sk_sleep(sk), &wait);
324 while (!(nsk = bt_accept_dequeue(sk, newsock))) {
325 set_current_state(TASK_INTERRUPTIBLE);
326 if (!timeo) {
327 err = -EAGAIN;
328 break;
329 }
330
331 release_sock(sk);
332 timeo = schedule_timeout(timeo);
333 lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
334
335 if (sk->sk_state != BT_LISTEN) {
336 err = -EBADFD;
337 break;
338 }
339
340 if (signal_pending(current)) {
341 err = sock_intr_errno(timeo);
342 break;
343 }
344 }
345 set_current_state(TASK_RUNNING);
346 remove_wait_queue(sk_sleep(sk), &wait);
347
348 if (err)
349 goto done;
350
351 newsock->state = SS_CONNECTED;
352
353 BT_DBG("new socket %p", nsk);
354
355done:
356 release_sock(sk);
357 return err;
358}
359
Gustavo F. Padovand7175d52011-02-04 02:43:46 -0200360static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
361{
362 struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
363 struct sock *sk = sock->sk;
364
365 BT_DBG("sock %p, sk %p", sock, sk);
366
367 addr->sa_family = AF_BLUETOOTH;
368 *len = sizeof(struct sockaddr_l2);
369
370 if (peer) {
371 la->l2_psm = l2cap_pi(sk)->psm;
372 bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst);
373 la->l2_cid = cpu_to_le16(l2cap_pi(sk)->dcid);
374 } else {
375 la->l2_psm = l2cap_pi(sk)->sport;
376 bacpy(&la->l2_bdaddr, &bt_sk(sk)->src);
377 la->l2_cid = cpu_to_le16(l2cap_pi(sk)->scid);
378 }
379
380 return 0;
381}
382
Gustavo F. Padovan99f48082011-02-04 02:52:55 -0200383static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
384{
385 struct sock *sk = sock->sk;
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300386 struct l2cap_chan *chan = l2cap_pi(sk)->chan;
Gustavo F. Padovan99f48082011-02-04 02:52:55 -0200387 struct l2cap_options opts;
388 struct l2cap_conninfo cinfo;
389 int len, err = 0;
390 u32 opt;
391
392 BT_DBG("sk %p", sk);
393
394 if (get_user(len, optlen))
395 return -EFAULT;
396
397 lock_sock(sk);
398
399 switch (optname) {
400 case L2CAP_OPTIONS:
Vasiliy Kulikove3fb5922011-02-10 20:59:42 +0300401 memset(&opts, 0, sizeof(opts));
Gustavo F. Padovan99f48082011-02-04 02:52:55 -0200402 opts.imtu = l2cap_pi(sk)->imtu;
403 opts.omtu = l2cap_pi(sk)->omtu;
404 opts.flush_to = l2cap_pi(sk)->flush_to;
405 opts.mode = l2cap_pi(sk)->mode;
406 opts.fcs = l2cap_pi(sk)->fcs;
407 opts.max_tx = l2cap_pi(sk)->max_tx;
408 opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win;
409
410 len = min_t(unsigned int, len, sizeof(opts));
411 if (copy_to_user(optval, (char *) &opts, len))
412 err = -EFAULT;
413
414 break;
415
416 case L2CAP_LM:
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300417 switch (chan->sec_level) {
Gustavo F. Padovan99f48082011-02-04 02:52:55 -0200418 case BT_SECURITY_LOW:
419 opt = L2CAP_LM_AUTH;
420 break;
421 case BT_SECURITY_MEDIUM:
422 opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT;
423 break;
424 case BT_SECURITY_HIGH:
425 opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
426 L2CAP_LM_SECURE;
427 break;
428 default:
429 opt = 0;
430 break;
431 }
432
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300433 if (chan->role_switch)
Gustavo F. Padovan99f48082011-02-04 02:52:55 -0200434 opt |= L2CAP_LM_MASTER;
435
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300436 if (chan->force_reliable)
Gustavo F. Padovan99f48082011-02-04 02:52:55 -0200437 opt |= L2CAP_LM_RELIABLE;
438
439 if (put_user(opt, (u32 __user *) optval))
440 err = -EFAULT;
441 break;
442
443 case L2CAP_CONNINFO:
444 if (sk->sk_state != BT_CONNECTED &&
445 !(sk->sk_state == BT_CONNECT2 &&
446 bt_sk(sk)->defer_setup)) {
447 err = -ENOTCONN;
448 break;
449 }
450
451 cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle;
452 memcpy(cinfo.dev_class, l2cap_pi(sk)->conn->hcon->dev_class, 3);
453
454 len = min_t(unsigned int, len, sizeof(cinfo));
455 if (copy_to_user(optval, (char *) &cinfo, len))
456 err = -EFAULT;
457
458 break;
459
460 default:
461 err = -ENOPROTOOPT;
462 break;
463 }
464
465 release_sock(sk);
466 return err;
467}
468
469static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
470{
471 struct sock *sk = sock->sk;
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300472 struct l2cap_chan *chan = l2cap_pi(sk)->chan;
Gustavo F. Padovan99f48082011-02-04 02:52:55 -0200473 struct bt_security sec;
474 int len, err = 0;
475
476 BT_DBG("sk %p", sk);
477
478 if (level == SOL_L2CAP)
479 return l2cap_sock_getsockopt_old(sock, optname, optval, optlen);
480
481 if (level != SOL_BLUETOOTH)
482 return -ENOPROTOOPT;
483
484 if (get_user(len, optlen))
485 return -EFAULT;
486
487 lock_sock(sk);
488
489 switch (optname) {
490 case BT_SECURITY:
491 if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM
492 && sk->sk_type != SOCK_RAW) {
493 err = -EINVAL;
494 break;
495 }
496
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300497 sec.level = chan->sec_level;
Gustavo F. Padovan99f48082011-02-04 02:52:55 -0200498
499 len = min_t(unsigned int, len, sizeof(sec));
500 if (copy_to_user(optval, (char *) &sec, len))
501 err = -EFAULT;
502
503 break;
504
505 case BT_DEFER_SETUP:
506 if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
507 err = -EINVAL;
508 break;
509 }
510
511 if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval))
512 err = -EFAULT;
513
514 break;
515
516 case BT_FLUSHABLE:
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300517 if (put_user(chan->flushable, (u32 __user *) optval))
Gustavo F. Padovan99f48082011-02-04 02:52:55 -0200518 err = -EFAULT;
519
520 break;
521
522 default:
523 err = -ENOPROTOOPT;
524 break;
525 }
526
527 release_sock(sk);
528 return err;
529}
530
Gustavo F. Padovan33575df2011-02-04 02:48:48 -0200531static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen)
532{
533 struct sock *sk = sock->sk;
Gustavo F. Padovanb4450032011-04-12 18:15:09 -0300534 struct l2cap_chan *chan = l2cap_pi(sk)->chan;
Gustavo F. Padovan33575df2011-02-04 02:48:48 -0200535 struct l2cap_options opts;
536 int len, err = 0;
537 u32 opt;
538
539 BT_DBG("sk %p", sk);
540
541 lock_sock(sk);
542
543 switch (optname) {
544 case L2CAP_OPTIONS:
545 if (sk->sk_state == BT_CONNECTED) {
546 err = -EINVAL;
547 break;
548 }
549
550 opts.imtu = l2cap_pi(sk)->imtu;
551 opts.omtu = l2cap_pi(sk)->omtu;
552 opts.flush_to = l2cap_pi(sk)->flush_to;
553 opts.mode = l2cap_pi(sk)->mode;
554 opts.fcs = l2cap_pi(sk)->fcs;
555 opts.max_tx = l2cap_pi(sk)->max_tx;
556 opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win;
557
558 len = min_t(unsigned int, sizeof(opts), optlen);
559 if (copy_from_user((char *) &opts, optval, len)) {
560 err = -EFAULT;
561 break;
562 }
563
564 if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) {
565 err = -EINVAL;
566 break;
567 }
568
569 l2cap_pi(sk)->mode = opts.mode;
570 switch (l2cap_pi(sk)->mode) {
571 case L2CAP_MODE_BASIC:
Gustavo F. Padovanb4450032011-04-12 18:15:09 -0300572 chan->conf_state &= ~L2CAP_CONF_STATE2_DEVICE;
Gustavo F. Padovan33575df2011-02-04 02:48:48 -0200573 break;
574 case L2CAP_MODE_ERTM:
575 case L2CAP_MODE_STREAMING:
576 if (!disable_ertm)
577 break;
578 /* fall through */
579 default:
580 err = -EINVAL;
581 break;
582 }
583
584 l2cap_pi(sk)->imtu = opts.imtu;
585 l2cap_pi(sk)->omtu = opts.omtu;
586 l2cap_pi(sk)->fcs = opts.fcs;
587 l2cap_pi(sk)->max_tx = opts.max_tx;
588 l2cap_pi(sk)->tx_win = (__u8)opts.txwin_size;
589 break;
590
591 case L2CAP_LM:
592 if (get_user(opt, (u32 __user *) optval)) {
593 err = -EFAULT;
594 break;
595 }
596
597 if (opt & L2CAP_LM_AUTH)
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300598 chan->sec_level = BT_SECURITY_LOW;
Gustavo F. Padovan33575df2011-02-04 02:48:48 -0200599 if (opt & L2CAP_LM_ENCRYPT)
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300600 chan->sec_level = BT_SECURITY_MEDIUM;
Gustavo F. Padovan33575df2011-02-04 02:48:48 -0200601 if (opt & L2CAP_LM_SECURE)
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300602 chan->sec_level = BT_SECURITY_HIGH;
Gustavo F. Padovan33575df2011-02-04 02:48:48 -0200603
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300604 chan->role_switch = (opt & L2CAP_LM_MASTER);
605 chan->force_reliable = (opt & L2CAP_LM_RELIABLE);
Gustavo F. Padovan33575df2011-02-04 02:48:48 -0200606 break;
607
608 default:
609 err = -ENOPROTOOPT;
610 break;
611 }
612
613 release_sock(sk);
614 return err;
615}
616
617static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
618{
619 struct sock *sk = sock->sk;
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300620 struct l2cap_chan *chan = l2cap_pi(sk)->chan;
Gustavo F. Padovan33575df2011-02-04 02:48:48 -0200621 struct bt_security sec;
622 int len, err = 0;
623 u32 opt;
624
625 BT_DBG("sk %p", sk);
626
627 if (level == SOL_L2CAP)
628 return l2cap_sock_setsockopt_old(sock, optname, optval, optlen);
629
630 if (level != SOL_BLUETOOTH)
631 return -ENOPROTOOPT;
632
633 lock_sock(sk);
634
635 switch (optname) {
636 case BT_SECURITY:
637 if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM
638 && sk->sk_type != SOCK_RAW) {
639 err = -EINVAL;
640 break;
641 }
642
643 sec.level = BT_SECURITY_LOW;
644
645 len = min_t(unsigned int, sizeof(sec), optlen);
646 if (copy_from_user((char *) &sec, optval, len)) {
647 err = -EFAULT;
648 break;
649 }
650
651 if (sec.level < BT_SECURITY_LOW ||
652 sec.level > BT_SECURITY_HIGH) {
653 err = -EINVAL;
654 break;
655 }
656
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300657 chan->sec_level = sec.level;
Gustavo F. Padovan33575df2011-02-04 02:48:48 -0200658 break;
659
660 case BT_DEFER_SETUP:
661 if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
662 err = -EINVAL;
663 break;
664 }
665
666 if (get_user(opt, (u32 __user *) optval)) {
667 err = -EFAULT;
668 break;
669 }
670
671 bt_sk(sk)->defer_setup = opt;
672 break;
673
674 case BT_FLUSHABLE:
675 if (get_user(opt, (u32 __user *) optval)) {
676 err = -EFAULT;
677 break;
678 }
679
680 if (opt > BT_FLUSHABLE_ON) {
681 err = -EINVAL;
682 break;
683 }
684
685 if (opt == BT_FLUSHABLE_OFF) {
686 struct l2cap_conn *conn = l2cap_pi(sk)->conn;
687 /* proceed futher only when we have l2cap_conn and
688 No Flush support in the LM */
689 if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) {
690 err = -EINVAL;
691 break;
692 }
693 }
694
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300695 chan->flushable = opt;
Gustavo F. Padovan33575df2011-02-04 02:48:48 -0200696 break;
697
698 default:
699 err = -ENOPROTOOPT;
700 break;
701 }
702
703 release_sock(sk);
704 return err;
705}
706
Gustavo F. Padovanfd83ccd2011-02-04 03:20:52 -0200707static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len)
708{
709 struct sock *sk = sock->sk;
710 struct l2cap_pinfo *pi = l2cap_pi(sk);
711 struct sk_buff *skb;
712 u16 control;
713 int err;
714
715 BT_DBG("sock %p, sk %p", sock, sk);
716
717 err = sock_error(sk);
718 if (err)
719 return err;
720
721 if (msg->msg_flags & MSG_OOB)
722 return -EOPNOTSUPP;
723
724 lock_sock(sk);
725
726 if (sk->sk_state != BT_CONNECTED) {
727 err = -ENOTCONN;
728 goto done;
729 }
730
731 /* Connectionless channel */
732 if (sk->sk_type == SOCK_DGRAM) {
733 skb = l2cap_create_connless_pdu(sk, msg, len);
734 if (IS_ERR(skb)) {
735 err = PTR_ERR(skb);
736 } else {
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300737 l2cap_do_send(pi->chan, skb);
Gustavo F. Padovanfd83ccd2011-02-04 03:20:52 -0200738 err = len;
739 }
740 goto done;
741 }
742
743 switch (pi->mode) {
744 case L2CAP_MODE_BASIC:
745 /* Check outgoing MTU */
746 if (len > pi->omtu) {
747 err = -EMSGSIZE;
748 goto done;
749 }
750
751 /* Create a basic PDU */
752 skb = l2cap_create_basic_pdu(sk, msg, len);
753 if (IS_ERR(skb)) {
754 err = PTR_ERR(skb);
755 goto done;
756 }
757
Gustavo F. Padovan43434782011-04-12 18:31:57 -0300758 l2cap_do_send(pi->chan, skb);
Gustavo F. Padovanfd83ccd2011-02-04 03:20:52 -0200759 err = len;
760 break;
761
762 case L2CAP_MODE_ERTM:
763 case L2CAP_MODE_STREAMING:
764 /* Entire SDU fits into one PDU */
Gustavo F. Padovan2c03a7a2011-03-25 20:15:28 -0300765 if (len <= pi->chan->remote_mps) {
Gustavo F. Padovanfd83ccd2011-02-04 03:20:52 -0200766 control = L2CAP_SDU_UNSEGMENTED;
767 skb = l2cap_create_iframe_pdu(sk, msg, len, control, 0);
768 if (IS_ERR(skb)) {
769 err = PTR_ERR(skb);
770 goto done;
771 }
Gustavo F. Padovan58d35f82011-04-04 16:16:44 -0300772 __skb_queue_tail(&pi->chan->tx_q, skb);
Gustavo F. Padovanfd83ccd2011-02-04 03:20:52 -0200773
Gustavo F. Padovan58d35f82011-04-04 16:16:44 -0300774 if (pi->chan->tx_send_head == NULL)
775 pi->chan->tx_send_head = skb;
Gustavo F. Padovanfd83ccd2011-02-04 03:20:52 -0200776
777 } else {
778 /* Segment SDU into multiples PDUs */
Gustavo F. Padovan2c03a7a2011-03-25 20:15:28 -0300779 err = l2cap_sar_segment_sdu(pi->chan, msg, len);
Gustavo F. Padovanfd83ccd2011-02-04 03:20:52 -0200780 if (err < 0)
781 goto done;
782 }
783
784 if (pi->mode == L2CAP_MODE_STREAMING) {
Gustavo F. Padovan42e5c802011-03-25 19:58:34 -0300785 l2cap_streaming_send(pi->chan);
Gustavo F. Padovan525cd182011-03-25 19:43:39 -0300786 err = len;
787 break;
Gustavo F. Padovanfd83ccd2011-02-04 03:20:52 -0200788 }
789
Gustavo F. Padovan525cd182011-03-25 19:43:39 -0300790 if ((pi->chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
791 (pi->chan->conn_state & L2CAP_CONN_WAIT_F)) {
792 err = len;
793 break;
794 }
795 err = l2cap_ertm_send(pi->chan);
796
Gustavo F. Padovanfd83ccd2011-02-04 03:20:52 -0200797 if (err >= 0)
798 err = len;
799 break;
800
801 default:
802 BT_DBG("bad state %1.1x", pi->mode);
803 err = -EBADFD;
804 }
805
806done:
807 release_sock(sk);
808 return err;
809}
810
Gustavo F. Padovan68983252011-02-04 03:02:31 -0200811static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags)
812{
813 struct sock *sk = sock->sk;
814
815 lock_sock(sk);
816
817 if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) {
Gustavo F. Padovan710f9b02011-03-25 14:30:37 -0300818 __l2cap_connect_rsp_defer(sk);
Gustavo F. Padovan68983252011-02-04 03:02:31 -0200819 release_sock(sk);
820 return 0;
821 }
822
823 release_sock(sk);
824
825 if (sock->type == SOCK_STREAM)
826 return bt_sock_stream_recvmsg(iocb, sock, msg, len, flags);
827
828 return bt_sock_recvmsg(iocb, sock, msg, len, flags);
829}
830
Gustavo F. Padovan05fc1572011-02-04 03:26:01 -0200831/* Kill socket (only if zapped and orphan)
832 * Must be called on unlocked socket.
833 */
834void l2cap_sock_kill(struct sock *sk)
835{
836 if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket)
837 return;
838
839 BT_DBG("sk %p state %d", sk, sk->sk_state);
840
841 /* Kill poor orphan */
842 bt_sock_unlink(&l2cap_sk_list, sk);
843 sock_set_flag(sk, SOCK_DEAD);
844 sock_put(sk);
845}
846
Gustavo F. Padovan6de07022011-02-04 03:35:20 -0200847/* Must be called on unlocked socket. */
848static void l2cap_sock_close(struct sock *sk)
849{
850 l2cap_sock_clear_timer(sk);
851 lock_sock(sk);
852 __l2cap_sock_close(sk, ECONNRESET);
853 release_sock(sk);
854 l2cap_sock_kill(sk);
855}
856
857static void l2cap_sock_cleanup_listen(struct sock *parent)
858{
859 struct sock *sk;
860
861 BT_DBG("parent %p", parent);
862
863 /* Close not yet accepted channels */
864 while ((sk = bt_accept_dequeue(parent, NULL)))
865 l2cap_sock_close(sk);
866
867 parent->sk_state = BT_CLOSED;
868 sock_set_flag(parent, SOCK_ZAPPED);
869}
870
871void __l2cap_sock_close(struct sock *sk, int reason)
872{
Ville Tervo5589fa92011-02-10 22:38:51 -0300873 struct l2cap_conn *conn = l2cap_pi(sk)->conn;
Gustavo F. Padovane92c8e72011-04-01 00:53:45 -0300874 struct l2cap_chan *chan = l2cap_pi(sk)->chan;
Ville Tervo5589fa92011-02-10 22:38:51 -0300875
Gustavo F. Padovan6de07022011-02-04 03:35:20 -0200876 BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
877
878 switch (sk->sk_state) {
879 case BT_LISTEN:
880 l2cap_sock_cleanup_listen(sk);
881 break;
882
883 case BT_CONNECTED:
884 case BT_CONFIG:
Ville Tervo5589fa92011-02-10 22:38:51 -0300885 if ((sk->sk_type == SOCK_SEQPACKET ||
886 sk->sk_type == SOCK_STREAM) &&
887 conn->hcon->type == ACL_LINK) {
Gustavo F. Padovan6de07022011-02-04 03:35:20 -0200888 l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
Gustavo F. Padovane92c8e72011-04-01 00:53:45 -0300889 l2cap_send_disconn_req(conn, chan, reason);
Gustavo F. Padovan6de07022011-02-04 03:35:20 -0200890 } else
Gustavo F. Padovane92c8e72011-04-01 00:53:45 -0300891 l2cap_chan_del(chan, reason);
Gustavo F. Padovan6de07022011-02-04 03:35:20 -0200892 break;
893
894 case BT_CONNECT2:
Ville Tervo5589fa92011-02-10 22:38:51 -0300895 if ((sk->sk_type == SOCK_SEQPACKET ||
896 sk->sk_type == SOCK_STREAM) &&
897 conn->hcon->type == ACL_LINK) {
Gustavo F. Padovan6de07022011-02-04 03:35:20 -0200898 struct l2cap_conn_rsp rsp;
899 __u16 result;
900
901 if (bt_sk(sk)->defer_setup)
902 result = L2CAP_CR_SEC_BLOCK;
903 else
904 result = L2CAP_CR_BAD_PSM;
905
906 rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
907 rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
908 rsp.result = cpu_to_le16(result);
909 rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
Gustavo F. Padovane92c8e72011-04-01 00:53:45 -0300910 l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
911 sizeof(rsp), &rsp);
Andrei Emeltchenko34bd0272011-03-24 17:16:08 +0200912 }
913
Gustavo F. Padovane92c8e72011-04-01 00:53:45 -0300914 l2cap_chan_del(chan, reason);
Gustavo F. Padovan6de07022011-02-04 03:35:20 -0200915 break;
916
917 case BT_CONNECT:
918 case BT_DISCONN:
Gustavo F. Padovane92c8e72011-04-01 00:53:45 -0300919 l2cap_chan_del(chan, reason);
Gustavo F. Padovan6de07022011-02-04 03:35:20 -0200920 break;
921
922 default:
923 sock_set_flag(sk, SOCK_ZAPPED);
924 break;
925 }
926}
927
Gustavo F. Padovandcba0db2011-02-04 03:08:36 -0200928static int l2cap_sock_shutdown(struct socket *sock, int how)
929{
930 struct sock *sk = sock->sk;
931 int err = 0;
932
933 BT_DBG("sock %p, sk %p", sock, sk);
934
935 if (!sk)
936 return 0;
937
938 lock_sock(sk);
939 if (!sk->sk_shutdown) {
940 if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM)
941 err = __l2cap_wait_ack(sk);
942
943 sk->sk_shutdown = SHUTDOWN_MASK;
944 l2cap_sock_clear_timer(sk);
945 __l2cap_sock_close(sk, 0);
946
947 if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
948 err = bt_sock_wait_state(sk, BT_CLOSED,
949 sk->sk_lingertime);
950 }
951
952 if (!err && sk->sk_err)
953 err = -sk->sk_err;
954
955 release_sock(sk);
956 return err;
957}
958
Gustavo F. Padovan554f05b2011-02-04 02:36:42 -0200959static int l2cap_sock_release(struct socket *sock)
960{
961 struct sock *sk = sock->sk;
962 int err;
963
964 BT_DBG("sock %p, sk %p", sock, sk);
965
966 if (!sk)
967 return 0;
968
969 err = l2cap_sock_shutdown(sock, 2);
970
971 sock_orphan(sk);
972 l2cap_sock_kill(sk);
973 return err;
974}
975
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -0200976static void l2cap_sock_destruct(struct sock *sk)
977{
978 BT_DBG("sk %p", sk);
979
980 skb_queue_purge(&sk->sk_receive_queue);
981 skb_queue_purge(&sk->sk_write_queue);
982}
983
984void l2cap_sock_init(struct sock *sk, struct sock *parent)
985{
986 struct l2cap_pinfo *pi = l2cap_pi(sk);
Gustavo F. Padovanb4450032011-04-12 18:15:09 -0300987 struct l2cap_chan *chan = pi->chan;
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -0200988
989 BT_DBG("sk %p", sk);
990
991 if (parent) {
Gustavo F. Padovanb4450032011-04-12 18:15:09 -0300992 struct l2cap_chan *pchan = l2cap_pi(parent)->chan;
993
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -0200994 sk->sk_type = parent->sk_type;
995 bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup;
996
997 pi->imtu = l2cap_pi(parent)->imtu;
998 pi->omtu = l2cap_pi(parent)->omtu;
Gustavo F. Padovanb4450032011-04-12 18:15:09 -0300999 chan->conf_state = pchan->conf_state;
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001000 pi->mode = l2cap_pi(parent)->mode;
1001 pi->fcs = l2cap_pi(parent)->fcs;
1002 pi->max_tx = l2cap_pi(parent)->max_tx;
1003 pi->tx_win = l2cap_pi(parent)->tx_win;
Gustavo F. Padovan43434782011-04-12 18:31:57 -03001004 chan->sec_level = pchan->sec_level;
1005 chan->role_switch = pchan->role_switch;
1006 chan->force_reliable = pchan->force_reliable;
1007 chan->flushable = pchan->flushable;
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001008 } else {
1009 pi->imtu = L2CAP_DEFAULT_MTU;
1010 pi->omtu = 0;
1011 if (!disable_ertm && sk->sk_type == SOCK_STREAM) {
1012 pi->mode = L2CAP_MODE_ERTM;
Gustavo F. Padovanb4450032011-04-12 18:15:09 -03001013 chan->conf_state |= L2CAP_CONF_STATE2_DEVICE;
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001014 } else {
1015 pi->mode = L2CAP_MODE_BASIC;
1016 }
1017 pi->max_tx = L2CAP_DEFAULT_MAX_TX;
1018 pi->fcs = L2CAP_FCS_CRC16;
1019 pi->tx_win = L2CAP_DEFAULT_TX_WINDOW;
Gustavo F. Padovan43434782011-04-12 18:31:57 -03001020 chan->sec_level = BT_SECURITY_LOW;
1021 chan->role_switch = 0;
1022 chan->force_reliable = 0;
1023 chan->flushable = BT_FLUSHABLE_OFF;
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001024 }
1025
1026 /* Default config options */
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001027 pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001028}
1029
1030static struct proto l2cap_proto = {
1031 .name = "L2CAP",
1032 .owner = THIS_MODULE,
1033 .obj_size = sizeof(struct l2cap_pinfo)
1034};
1035
1036struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
1037{
1038 struct sock *sk;
1039
1040 sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto);
1041 if (!sk)
1042 return NULL;
1043
1044 sock_init_data(sock, sk);
1045 INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
1046
1047 sk->sk_destruct = l2cap_sock_destruct;
1048 sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT);
1049
1050 sock_reset_flag(sk, SOCK_ZAPPED);
1051
1052 sk->sk_protocol = proto;
1053 sk->sk_state = BT_OPEN;
1054
1055 setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk);
1056
1057 bt_sock_link(&l2cap_sk_list, sk);
1058 return sk;
1059}
1060
1061static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
1062 int kern)
1063{
1064 struct sock *sk;
Gustavo F. Padovan5d41ce12011-04-08 15:40:02 -03001065 struct l2cap_chan *chan;
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001066
1067 BT_DBG("sock %p", sock);
1068
1069 sock->state = SS_UNCONNECTED;
1070
1071 if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM &&
1072 sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
1073 return -ESOCKTNOSUPPORT;
1074
1075 if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))
1076 return -EPERM;
1077
1078 sock->ops = &l2cap_sock_ops;
1079
1080 sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC);
1081 if (!sk)
1082 return -ENOMEM;
1083
Gustavo F. Padovan5d41ce12011-04-08 15:40:02 -03001084 chan = l2cap_chan_alloc(sk);
1085 if (!chan) {
1086 l2cap_sock_kill(sk);
1087 return -ENOMEM;
1088 }
1089
1090 l2cap_pi(sk)->chan = chan;
1091
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001092 l2cap_sock_init(sk, NULL);
1093 return 0;
1094}
1095
Gustavo F. Padovan65390582011-02-04 02:33:56 -02001096const struct proto_ops l2cap_sock_ops = {
1097 .family = PF_BLUETOOTH,
1098 .owner = THIS_MODULE,
1099 .release = l2cap_sock_release,
1100 .bind = l2cap_sock_bind,
1101 .connect = l2cap_sock_connect,
1102 .listen = l2cap_sock_listen,
1103 .accept = l2cap_sock_accept,
1104 .getname = l2cap_sock_getname,
1105 .sendmsg = l2cap_sock_sendmsg,
1106 .recvmsg = l2cap_sock_recvmsg,
1107 .poll = bt_sock_poll,
1108 .ioctl = bt_sock_ioctl,
1109 .mmap = sock_no_mmap,
1110 .socketpair = sock_no_socketpair,
1111 .shutdown = l2cap_sock_shutdown,
1112 .setsockopt = l2cap_sock_setsockopt,
1113 .getsockopt = l2cap_sock_getsockopt
1114};
1115
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001116static const struct net_proto_family l2cap_sock_family_ops = {
1117 .family = PF_BLUETOOTH,
1118 .owner = THIS_MODULE,
1119 .create = l2cap_sock_create,
1120};
1121
1122int __init l2cap_init_sockets(void)
1123{
Gustavo F. Padovane2174ca2011-02-17 19:16:55 -03001124 int err;
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001125
Gustavo F. Padovane2174ca2011-02-17 19:16:55 -03001126 err = proto_register(&l2cap_proto, 0);
1127 if (err < 0)
1128 return err;
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001129
Gustavo F. Padovane2174ca2011-02-17 19:16:55 -03001130 err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops);
1131 if (err < 0)
1132 goto error;
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001133
Gustavo F. Padovane2174ca2011-02-17 19:16:55 -03001134 BT_INFO("L2CAP socket layer initialized");
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001135
Gustavo F. Padovane2174ca2011-02-17 19:16:55 -03001136 return 0;
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001137
1138error:
Gustavo F. Padovane2174ca2011-02-17 19:16:55 -03001139 BT_ERR("L2CAP socket registration failed");
1140 proto_unregister(&l2cap_proto);
1141 return err;
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001142}
1143
1144void l2cap_cleanup_sockets(void)
1145{
Gustavo F. Padovane2174ca2011-02-17 19:16:55 -03001146 if (bt_sock_unregister(BTPROTO_L2CAP) < 0)
1147 BT_ERR("L2CAP socket unregistration failed");
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001148
Gustavo F. Padovane2174ca2011-02-17 19:16:55 -03001149 proto_unregister(&l2cap_proto);
Gustavo F. Padovanbb58f742011-02-03 20:50:35 -02001150}