⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sco.c

📁 Linux嵌入式平台蓝牙协议栈软件
💻 C
📖 第 1 页 / 共 2 页
字号:
/*    BlueZ - Bluetooth protocol stack for Linux   Copyright (C) 2000-2001 Qualcomm Incorporated   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 SCO sockets. */#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/fcntl.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/socket.h>#include <linux/skbuff.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/list.h>#include <net/sock.h>#include <asm/system.h>#include <asm/uaccess.h>#include <net/bluetooth/bluetooth.h>#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.3"static struct proto_ops sco_sock_ops;static struct bt_sock_list sco_sk_list = {	.lock = RW_LOCK_UNLOCKED};static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);static void sco_chan_del(struct sock *sk, int err);static int  sco_conn_del(struct hci_conn *conn, int err);static void sco_sock_close(struct sock *sk);static void sco_sock_kill(struct sock *sk);/* ---- SCO timers ---- */static void sco_sock_timeout(unsigned long arg){	struct sock *sk = (struct sock *) arg;	BT_DBG("sock %p state %d", sk, sk->sk_state);	bh_lock_sock(sk);	sk->sk_err = ETIMEDOUT;	sk->sk_state_change(sk);	bh_unlock_sock(sk);	sco_sock_kill(sk);	sock_put(sk);}static void sco_sock_set_timer(struct sock *sk, long timeout){	BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout);	sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout);}static void sco_sock_clear_timer(struct sock *sk){	BT_DBG("sock %p state %d", sk, sk->sk_state);	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 hci_dev *hdev = hcon->hdev;	struct sco_conn *conn;	if ((conn = hcon->sco_data))		return conn;	if (status)		return conn;	if (!(conn = kmalloc(sizeof(struct sco_conn), GFP_ATOMIC)))		return NULL;	memset(conn, 0, sizeof(struct sco_conn));	spin_lock_init(&conn->lock);	hcon->sco_data = conn;	conn->hcon = hcon;	conn->src = &hdev->bdaddr;	conn->dst = &hcon->dst;	if (hdev->sco_mtu > 0)		conn->mtu = hdev->sco_mtu;	else		conn->mtu = 60;	BT_DBG("hcon %p conn %p", hcon, conn);	return conn;}static inline struct sock *sco_chan_get(struct sco_conn *conn){	struct sock *sk = NULL;	sco_conn_lock(conn);	sk = conn->sk;	sco_conn_unlock(conn);	return sk;}static int sco_conn_del(struct hci_conn *hcon, int err){	struct sco_conn *conn;	struct sock *sk;	if (!(conn = hcon->sco_data)) 		return 0;	BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);	/* Kill socket */	if ((sk = sco_chan_get(conn))) {		bh_lock_sock(sk);		sco_sock_clear_timer(sk);		sco_chan_del(sk, err);		bh_unlock_sock(sk);		sco_sock_kill(sk);	}	hcon->sco_data = NULL;	kfree(conn);	return 0;}static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent){	int err = 0;	sco_conn_lock(conn);	if (conn->sk) {		err = -EBUSY;	} else {		__sco_chan_add(conn, sk, parent);	}	sco_conn_unlock(conn);	return err;}static int sco_connect(struct sock *sk){	bdaddr_t *src = &bt_sk(sk)->src;	bdaddr_t *dst = &bt_sk(sk)->dst;	struct sco_conn *conn;	struct hci_conn *hcon;	struct hci_dev  *hdev;	int err = 0;	BT_DBG("%s -> %s", batostr(src), batostr(dst));	if (!(hdev = hci_get_route(dst, src)))		return -EHOSTUNREACH;	hci_dev_lock_bh(hdev);	err = -ENOMEM;	hcon = hci_connect(hdev, SCO_LINK, dst);	if (!hcon)		goto done;	conn = sco_conn_add(hcon, 0);	if (!conn) {		hci_conn_put(hcon);		goto done;	}	/* Update source addr of the socket */	bacpy(src, conn->src);	err = sco_chan_add(conn, sk, NULL);	if (err)		goto done;	if (hcon->state == BT_CONNECTED) {		sco_sock_clear_timer(sk);		sk->sk_state = BT_CONNECTED;	} else {		sk->sk_state = BT_CONNECT;		sco_sock_set_timer(sk, sk->sk_sndtimeo);	}done:	hci_dev_unlock_bh(hdev);	hci_dev_put(hdev);	return err;}static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len){	struct sco_conn *conn = sco_pi(sk)->conn;	struct sk_buff *skb;	int err, count;	/* Check outgoing MTU */	if (len > conn->mtu)		return -EINVAL;	BT_DBG("sk %p len %d", sk, len);	count = min_t(unsigned int, conn->mtu, len);	if (!(skb = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err)))		return err;	if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {		err = -EFAULT;		goto fail;	}	if ((err = hci_send_sco(conn->hcon, skb)) < 0)		goto fail;	return count;fail:	kfree_skb(skb);	return err;}static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb){	struct sock *sk = sco_chan_get(conn);	if (!sk)		goto drop;	BT_DBG("sk %p len %d", sk, skb->len);	if (sk->sk_state != BT_CONNECTED)		goto drop;	if (!sock_queue_rcv_skb(sk, skb))		return;drop:	kfree_skb(skb);	return;}/* -------- Socket interface ---------- */static struct sock *__sco_get_sock_by_addr(bdaddr_t *ba){	struct sock *sk;	struct hlist_node *node;	sk_for_each(sk, node, &sco_sk_list.head)		if (!bacmp(&bt_sk(sk)->src, ba))			goto found;	sk = NULL;found:	return sk;}/* Find socket listening on source bdaddr. * Returns closest match. */static struct sock *sco_get_sock_listen(bdaddr_t *src){	struct sock *sk = NULL, *sk1 = NULL;	struct hlist_node *node;	read_lock(&sco_sk_list.lock);	sk_for_each(sk, node, &sco_sk_list.head) {		if (sk->sk_state != BT_LISTEN)			continue;		/* Exact match. */		if (!bacmp(&bt_sk(sk)->src, src))			break;		/* Closest match */		if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))			sk1 = sk;	}	read_unlock(&sco_sk_list.lock);	return node ? sk : sk1;}static void sco_sock_destruct(struct sock *sk){	BT_DBG("sk %p", sk);	skb_queue_purge(&sk->sk_receive_queue);	skb_queue_purge(&sk->sk_write_queue);	if (sk->sk_protinfo)		kfree(sk->sk_protinfo);}static void sco_sock_cleanup_listen(struct sock *parent){	struct sock *sk;	BT_DBG("parent %p", parent);	/* Close not yet accepted channels */	while ((sk = bt_accept_dequeue(parent, NULL))) {		sco_sock_close(sk);		sco_sock_kill(sk);	}	parent->sk_state  = BT_CLOSED;	parent->sk_zapped = 1;}/* Kill socket (only if zapped and orphan) * Must be called on unlocked socket. */static void sco_sock_kill(struct sock *sk){	if (!sk->sk_zapped || sk->sk_socket)		return;	BT_DBG("sk %p state %d", sk, sk->sk_state);	/* Kill poor orphan */	bt_sock_unlink(&sco_sk_list, sk);	sock_set_flag(sk, SOCK_DEAD);	sock_put(sk);}/* Close socket. * Must be called on unlocked socket. */static void sco_sock_close(struct sock *sk){	struct sco_conn *conn;	sco_sock_clear_timer(sk);	lock_sock(sk);	conn = sco_pi(sk)->conn;	BT_DBG("sk %p state %d conn %p socket %p", sk, sk->sk_state, conn, sk->sk_socket);	switch (sk->sk_state) {	case BT_LISTEN:		sco_sock_cleanup_listen(sk);		break;	case BT_CONNECTED:	case BT_CONFIG:	case BT_CONNECT:	case BT_DISCONN:		sco_chan_del(sk, ECONNRESET);		break;	default:		sk->sk_zapped = 1;		break;	};	release_sock(sk);	sco_sock_kill(sk);}static void sco_sock_init(struct sock *sk, struct sock *parent){	BT_DBG("sk %p", sk);	if (parent) 		sk->sk_type = parent->sk_type;}static struct sock *sco_sock_alloc(struct socket *sock, int proto, int prio){	struct sock *sk;	sk = bt_sock_alloc(sock, proto, sizeof(struct sco_pinfo), prio);	if (!sk)		return NULL;	sk_set_owner(sk, THIS_MODULE);	sk->sk_destruct = sco_sock_destruct;	sk->sk_sndtimeo = SCO_CONN_TIMEOUT;	sk->sk_state    = BT_OPEN;	sco_sock_init_timer(sk);	bt_sock_link(&sco_sk_list, sk);	return sk;}static int sco_sock_create(struct socket *sock, int protocol){	struct sock *sk;	BT_DBG("sock %p", sock);	sock->state = SS_UNCONNECTED;	if (sock->type != SOCK_SEQPACKET)		return -ESOCKTNOSUPPORT;	sock->ops = &sco_sock_ops;	if (!(sk = sco_sock_alloc(sock, protocol, GFP_KERNEL)))		return -ENOMEM;	sco_sock_init(sk, NULL);	return 0;}static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len){	struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;	struct sock *sk = sock->sk;	bdaddr_t *src = &sa->sco_bdaddr;	int err = 0;	BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr));	if (!addr || addr->sa_family != AF_BLUETOOTH)		return -EINVAL;	lock_sock(sk);	if (sk->sk_state != BT_OPEN) {		err = -EBADFD;		goto done;	}	write_lock_bh(&sco_sk_list.lock);	if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) {		err = -EADDRINUSE;	} else {		/* Save source address */		bacpy(&bt_sk(sk)->src, &sa->sco_bdaddr);		sk->sk_state = BT_BOUND;	}	write_unlock_bh(&sco_sk_list.lock);done:	release_sock(sk);	return err;}static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags){	struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;	struct sock *sk = sock->sk;	int err = 0;	BT_DBG("sk %p", sk);	if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_sco))		return -EINVAL;	if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND)		return -EBADFD;	if (sk->sk_type != SOCK_SEQPACKET)		return -EINVAL;	lock_sock(sk);	/* Set destination address and psm */	bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr);	if ((err = sco_connect(sk)))		goto done;	err = bt_sock_wait_state(sk, BT_CONNECTED, 			sock_sndtimeo(sk, flags & O_NONBLOCK));done:	release_sock(sk);	return err;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -