-/*
+/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2000-2001 Qualcomm Incorporated
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
+ 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
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/sco.h>
-#ifndef CONFIG_BT_SCO_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
+#define VERSION "0.6"
-#define VERSION "0.5"
+static int disable_esco = 0;
static const struct proto_ops sco_sock_ops;
static struct bt_sock_list sco_sk_list = {
- .lock = RW_LOCK_UNLOCKED
+ .lock = __RW_LOCK_UNLOCKED(sco_sk_list.lock)
};
static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
sk_stop_timer(sk, &sk->sk_timer);
}
-static void sco_sock_init_timer(struct sock *sk)
-{
- init_timer(&sk->sk_timer);
- sk->sk_timer.function = sco_sock_timeout;
- sk->sk_timer.data = (unsigned long)sk;
-}
-
/* ---- SCO connections ---- */
static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status)
{
struct sco_conn *conn;
struct sock *sk;
- if (!(conn = hcon->sco_data))
+ if (!(conn = hcon->sco_data))
return 0;
BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
struct sco_conn *conn;
struct hci_conn *hcon;
struct hci_dev *hdev;
- int err = 0;
+ int err, type;
BT_DBG("%s -> %s", batostr(src), batostr(dst));
err = -ENOMEM;
- hcon = hci_connect(hdev, SCO_LINK, dst);
+ if (lmp_esco_capable(hdev) && !disable_esco)
+ type = ESCO_LINK;
+ else
+ type = SCO_LINK;
+
+ hcon = hci_connect(hdev, type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING);
if (!hcon)
goto done;
sk->sk_state = BT_CONNECT;
sco_sock_set_timer(sk, sk->sk_sndtimeo);
}
+
done:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
default:
sock_set_flag(sk, SOCK_ZAPPED);
break;
- };
+ }
release_sock(sk);
{
BT_DBG("sk %p", sk);
- if (parent)
+ if (parent)
sk->sk_type = parent->sk_type;
}
.obj_size = sizeof(struct sco_pinfo)
};
-static struct sock *sco_sock_alloc(struct socket *sock, int proto, gfp_t prio)
+static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
{
struct sock *sk;
- sk = sk_alloc(PF_BLUETOOTH, prio, &sco_proto, 1);
+ sk = sk_alloc(net, PF_BLUETOOTH, prio, &sco_proto);
if (!sk)
return NULL;
sk->sk_protocol = proto;
sk->sk_state = BT_OPEN;
- sco_sock_init_timer(sk);
+ setup_timer(&sk->sk_timer, sco_sock_timeout, (unsigned long)sk);
bt_sock_link(&sco_sk_list, sk);
return sk;
}
-static int sco_sock_create(struct socket *sock, int protocol)
+static int sco_sock_create(struct net *net, struct socket *sock, int protocol)
{
struct sock *sk;
sock->ops = &sco_sock_ops;
- sk = sco_sock_alloc(sock, protocol, GFP_ATOMIC);
+ sk = sco_sock_alloc(net, sock, protocol, GFP_ATOMIC);
if (!sk)
return -ENOMEM;
if ((err = sco_connect(sk)))
goto done;
- err = bt_sock_wait_state(sk, BT_CONNECTED,
+ err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
done:
return 0;
}
-static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
+static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
return err;
}
-static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
+static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
struct sco_options opts;
struct sco_conninfo cinfo;
- int len, err = 0;
+ int len, err = 0;
BT_DBG("sk %p", sk);
return err;
}
+static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
+{
+ struct sock *sk = sock->sk;
+ int len, err = 0;
+
+ BT_DBG("sk %p", sk);
+
+ if (level == SOL_SCO)
+ return sco_sock_getsockopt_old(sock, optname, optval, optlen);
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+
+ lock_sock(sk);
+
+ switch (optname) {
+ default:
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ release_sock(sk);
+ return err;
+}
+
static int sco_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
bt_accept_enqueue(parent, sk);
}
-/* Delete channel.
+/* Delete channel.
* Must be called on the locked socket. */
static void sco_chan_del(struct sock *sk, int err)
{
BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
- if (conn) {
+ if (conn) {
sco_conn_lock(conn);
conn->sk = NULL;
sco_pi(sk)->conn = NULL;
bh_lock_sock(parent);
- sk = sco_sock_alloc(NULL, BTPROTO_SCO, GFP_ATOMIC);
+ sk = sco_sock_alloc(sock_net(parent), NULL, BTPROTO_SCO, GFP_ATOMIC);
if (!sk) {
bh_unlock_sock(parent);
goto done;
/* ----- SCO interface with lower layer (HCI) ----- */
static int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
{
+ register struct sock *sk;
+ struct hlist_node *node;
+ int lm = 0;
+
+ if (type != SCO_LINK && type != ESCO_LINK)
+ return 0;
+
BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
- /* Always accept connection */
- return HCI_LM_ACCEPT;
+ /* Find listening sockets */
+ read_lock(&sco_sk_list.lock);
+ sk_for_each(sk, node, &sco_sk_list.head) {
+ if (sk->sk_state != BT_LISTEN)
+ continue;
+
+ if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
+ !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
+ lm |= HCI_LM_ACCEPT;
+ break;
+ }
+ }
+ read_unlock(&sco_sk_list.lock);
+
+ return lm;
}
static int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
{
BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
- if (hcon->type != SCO_LINK)
+ if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
return 0;
if (!status) {
conn = sco_conn_add(hcon, status);
if (conn)
sco_conn_ready(conn);
- } else
+ } else
sco_conn_del(hcon, bt_err(status));
return 0;
}
-static int sco_disconn_ind(struct hci_conn *hcon, __u8 reason)
+static int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
{
BT_DBG("hcon %p reason %d", hcon, reason);
- if (hcon->type != SCO_LINK)
+ if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
return 0;
sco_conn_del(hcon, bt_err(reason));
+
return 0;
}
}
drop:
- kfree_skb(skb);
+ kfree_skb(skb);
return 0;
}
.sendmsg = sco_sock_sendmsg,
.recvmsg = bt_sock_recvmsg,
.poll = bt_sock_poll,
- .ioctl = sock_no_ioctl,
+ .ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair,
.shutdown = sock_no_shutdown,
.id = HCI_PROTO_SCO,
.connect_ind = sco_connect_ind,
.connect_cfm = sco_connect_cfm,
- .disconn_ind = sco_disconn_ind,
+ .disconn_cfm = sco_disconn_cfm,
.recv_scodata = sco_recv_scodata
};
module_init(sco_init);
module_exit(sco_exit);
-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
+module_param(disable_esco, bool, 0644);
+MODULE_PARM_DESC(disable_esco, "Disable eSCO connection creation");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");