| /* |
| BlueZ - Bluetooth protocol stack for Linux |
| Copyright (C) 2000-2001 Qualcomm Incorporated |
| Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org> |
| Copyright (C) 2010 Google Inc. |
| |
| Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License version 2 as |
| published by the Free Software Foundation; |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. |
| IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY |
| CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES |
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, |
| COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS |
| SOFTWARE IS DISCLAIMED. |
| */ |
| |
| /* Bluetooth L2CAP sockets. */ |
| |
| #include <net/bluetooth/bluetooth.h> |
| #include <net/bluetooth/l2cap.h> |
| |
| static void l2cap_sock_timeout(unsigned long arg) |
| { |
| struct sock *sk = (struct sock *) arg; |
| int reason; |
| |
| BT_DBG("sock %p state %d", sk, sk->sk_state); |
| |
| bh_lock_sock(sk); |
| |
| if (sock_owned_by_user(sk)) { |
| /* sk is owned by user. Try again later */ |
| l2cap_sock_set_timer(sk, HZ / 5); |
| bh_unlock_sock(sk); |
| sock_put(sk); |
| return; |
| } |
| |
| if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) |
| reason = ECONNREFUSED; |
| else if (sk->sk_state == BT_CONNECT && |
| l2cap_pi(sk)->sec_level != BT_SECURITY_SDP) |
| reason = ECONNREFUSED; |
| else |
| reason = ETIMEDOUT; |
| |
| __l2cap_sock_close(sk, reason); |
| |
| bh_unlock_sock(sk); |
| |
| l2cap_sock_kill(sk); |
| sock_put(sk); |
| } |
| |
| static int l2cap_sock_release(struct socket *sock) |
| { |
| struct sock *sk = sock->sk; |
| int err; |
| |
| BT_DBG("sock %p, sk %p", sock, sk); |
| |
| if (!sk) |
| return 0; |
| |
| err = l2cap_sock_shutdown(sock, 2); |
| |
| sock_orphan(sk); |
| l2cap_sock_kill(sk); |
| return err; |
| } |
| |
| static void l2cap_sock_destruct(struct sock *sk) |
| { |
| BT_DBG("sk %p", sk); |
| |
| skb_queue_purge(&sk->sk_receive_queue); |
| skb_queue_purge(&sk->sk_write_queue); |
| } |
| |
| void l2cap_sock_init(struct sock *sk, struct sock *parent) |
| { |
| struct l2cap_pinfo *pi = l2cap_pi(sk); |
| |
| BT_DBG("sk %p", sk); |
| |
| if (parent) { |
| sk->sk_type = parent->sk_type; |
| bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup; |
| |
| pi->imtu = l2cap_pi(parent)->imtu; |
| pi->omtu = l2cap_pi(parent)->omtu; |
| pi->conf_state = l2cap_pi(parent)->conf_state; |
| pi->mode = l2cap_pi(parent)->mode; |
| pi->fcs = l2cap_pi(parent)->fcs; |
| pi->max_tx = l2cap_pi(parent)->max_tx; |
| pi->tx_win = l2cap_pi(parent)->tx_win; |
| pi->sec_level = l2cap_pi(parent)->sec_level; |
| pi->role_switch = l2cap_pi(parent)->role_switch; |
| pi->force_reliable = l2cap_pi(parent)->force_reliable; |
| pi->flushable = l2cap_pi(parent)->flushable; |
| } else { |
| pi->imtu = L2CAP_DEFAULT_MTU; |
| pi->omtu = 0; |
| if (!disable_ertm && sk->sk_type == SOCK_STREAM) { |
| pi->mode = L2CAP_MODE_ERTM; |
| pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; |
| } else { |
| pi->mode = L2CAP_MODE_BASIC; |
| } |
| pi->max_tx = L2CAP_DEFAULT_MAX_TX; |
| pi->fcs = L2CAP_FCS_CRC16; |
| pi->tx_win = L2CAP_DEFAULT_TX_WINDOW; |
| pi->sec_level = BT_SECURITY_LOW; |
| pi->role_switch = 0; |
| pi->force_reliable = 0; |
| pi->flushable = BT_FLUSHABLE_OFF; |
| } |
| |
| /* Default config options */ |
| pi->conf_len = 0; |
| pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; |
| skb_queue_head_init(TX_QUEUE(sk)); |
| skb_queue_head_init(SREJ_QUEUE(sk)); |
| skb_queue_head_init(BUSY_QUEUE(sk)); |
| INIT_LIST_HEAD(SREJ_LIST(sk)); |
| } |
| |
| static struct proto l2cap_proto = { |
| .name = "L2CAP", |
| .owner = THIS_MODULE, |
| .obj_size = sizeof(struct l2cap_pinfo) |
| }; |
| |
| struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) |
| { |
| struct sock *sk; |
| |
| sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto); |
| if (!sk) |
| return NULL; |
| |
| sock_init_data(sock, sk); |
| INIT_LIST_HEAD(&bt_sk(sk)->accept_q); |
| |
| sk->sk_destruct = l2cap_sock_destruct; |
| sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT); |
| |
| sock_reset_flag(sk, SOCK_ZAPPED); |
| |
| sk->sk_protocol = proto; |
| sk->sk_state = BT_OPEN; |
| |
| setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk); |
| |
| bt_sock_link(&l2cap_sk_list, sk); |
| return sk; |
| } |
| |
| static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, |
| int kern) |
| { |
| struct sock *sk; |
| |
| BT_DBG("sock %p", sock); |
| |
| sock->state = SS_UNCONNECTED; |
| |
| if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && |
| sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) |
| return -ESOCKTNOSUPPORT; |
| |
| if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) |
| return -EPERM; |
| |
| sock->ops = &l2cap_sock_ops; |
| |
| sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC); |
| if (!sk) |
| return -ENOMEM; |
| |
| l2cap_sock_init(sk, NULL); |
| return 0; |
| } |
| |
| const struct proto_ops l2cap_sock_ops = { |
| .family = PF_BLUETOOTH, |
| .owner = THIS_MODULE, |
| .release = l2cap_sock_release, |
| .bind = l2cap_sock_bind, |
| .connect = l2cap_sock_connect, |
| .listen = l2cap_sock_listen, |
| .accept = l2cap_sock_accept, |
| .getname = l2cap_sock_getname, |
| .sendmsg = l2cap_sock_sendmsg, |
| .recvmsg = l2cap_sock_recvmsg, |
| .poll = bt_sock_poll, |
| .ioctl = bt_sock_ioctl, |
| .mmap = sock_no_mmap, |
| .socketpair = sock_no_socketpair, |
| .shutdown = l2cap_sock_shutdown, |
| .setsockopt = l2cap_sock_setsockopt, |
| .getsockopt = l2cap_sock_getsockopt |
| }; |
| |
| static const struct net_proto_family l2cap_sock_family_ops = { |
| .family = PF_BLUETOOTH, |
| .owner = THIS_MODULE, |
| .create = l2cap_sock_create, |
| }; |
| |
| int __init l2cap_init_sockets(void) |
| { |
| int err; |
| |
| err = proto_register(&l2cap_proto, 0); |
| if (err < 0) |
| return err; |
| |
| err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops); |
| if (err < 0) |
| goto error; |
| |
| BT_INFO("L2CAP socket layer initialized"); |
| |
| return 0; |
| |
| error: |
| BT_ERR("L2CAP socket registration failed"); |
| proto_unregister(&l2cap_proto); |
| return err; |
| } |
| |
| void l2cap_cleanup_sockets(void) |
| { |
| if (bt_sock_unregister(BTPROTO_L2CAP) < 0) |
| BT_ERR("L2CAP socket unregistration failed"); |
| |
| proto_unregister(&l2cap_proto); |
| } |