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

📄 l2cap.c

📁 Linux嵌入式平台蓝牙协议栈软件
💻 C
📖 第 1 页 / 共 4 页
字号:
		if (err < 0)			goto done;	}	sk->sk_max_ack_backlog = backlog;	sk->sk_ack_backlog = 0;	sk->sk_state = BT_LISTEN;done:	release_sock(sk);	return err;}static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags){	DECLARE_WAITQUEUE(wait, current);	struct sock *sk = sock->sk, *nsk;	long timeo;	int err = 0;	lock_sock(sk);	if (sk->sk_state != BT_LISTEN) {		err = -EBADFD;		goto done;	}	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);	BT_DBG("sk %p timeo %ld", sk, timeo);	/* Wait for an incoming connection. (wake-one). */	add_wait_queue_exclusive(sk->sk_sleep, &wait);	while (!(nsk = bt_accept_dequeue(sk, newsock))) {		set_current_state(TASK_INTERRUPTIBLE);		if (!timeo) {			err = -EAGAIN;			break;		}		release_sock(sk);		timeo = schedule_timeout(timeo);		lock_sock(sk);		if (sk->sk_state != BT_LISTEN) {			err = -EBADFD;			break;		}		if (signal_pending(current)) {			err = sock_intr_errno(timeo);			break;		}	}	set_current_state(TASK_RUNNING);	remove_wait_queue(sk->sk_sleep, &wait);	if (err)		goto done;	newsock->state = SS_CONNECTED;	BT_DBG("new socket %p", nsk);done:	release_sock(sk);	return err;}static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer){	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;	struct sock *sk = sock->sk;	BT_DBG("sock %p, sk %p", sock, sk);	addr->sa_family = AF_BLUETOOTH;	*len = sizeof(struct sockaddr_l2);	if (peer)		bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst);	else		bacpy(&la->l2_bdaddr, &bt_sk(sk)->src);	la->l2_psm = l2cap_pi(sk)->psm;	return 0;}static inline int l2cap_do_send(struct sock *sk, struct msghdr *msg, int len){	struct l2cap_conn *conn = l2cap_pi(sk)->conn;	struct sk_buff *skb, **frag;	int err, hlen, count, sent=0;	struct l2cap_hdr *lh;	BT_DBG("sk %p len %d", sk, len);	/* First fragment (with L2CAP header) */	if (sk->sk_type == SOCK_DGRAM)		hlen = L2CAP_HDR_SIZE + 2;	else		hlen = L2CAP_HDR_SIZE;	count = min_t(unsigned int, (conn->mtu - hlen), len);	skb = bt_skb_send_alloc(sk, hlen + count,			msg->msg_flags & MSG_DONTWAIT, &err);	if (!skb)		return err;	/* Create L2CAP header */	lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);	lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);	lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));	if (sk->sk_type == SOCK_DGRAM)		put_unaligned(l2cap_pi(sk)->psm, (u16 *) skb_put(skb, 2));	if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {		err = -EFAULT;		goto fail;	}	sent += count;	len  -= count;	/* Continuation fragments (no L2CAP header) */	frag = &skb_shinfo(skb)->frag_list;	while (len) {		count = min_t(unsigned int, conn->mtu, len);		*frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);		if (!*frag)			goto fail;				if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) {			err = -EFAULT;			goto fail;		}		sent += count;		len  -= count;		frag = &(*frag)->next;	}	if ((err = hci_send_acl(conn->hcon, skb, 0)) < 0)		goto fail;	return sent;fail:	kfree_skb(skb);	return err;}static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len){	struct sock *sk = sock->sk;	int err = 0;	BT_DBG("sock %p, sk %p", sock, sk);	if (sk->sk_err)		return sock_error(sk);	if (msg->msg_flags & MSG_OOB)		return -EOPNOTSUPP;	/* Check outgoing MTU */	if (len > l2cap_pi(sk)->omtu)		return -EINVAL;	lock_sock(sk);	if (sk->sk_state == BT_CONNECTED)		err = l2cap_do_send(sk, msg, len);	else		err = -ENOTCONN;	release_sock(sk);	return err;}static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen){	struct sock *sk = sock->sk;	struct l2cap_options opts;	int err = 0, len;	u32 opt;	BT_DBG("sk %p", sk);	lock_sock(sk);	switch (optname) {	case L2CAP_OPTIONS:		len = min_t(unsigned int, sizeof(opts), optlen);		if (copy_from_user((char *)&opts, optval, len)) {			err = -EFAULT;			break;		}		l2cap_pi(sk)->imtu  = opts.imtu;		l2cap_pi(sk)->omtu  = opts.omtu;		break;	case L2CAP_LM:		if (get_user(opt, (u32 __user *)optval)) {			err = -EFAULT;			break;		}		l2cap_pi(sk)->link_mode = opt;		break;	default:		err = -ENOPROTOOPT;		break;	}	release_sock(sk);	return err;}static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen){	struct sock *sk = sock->sk;	struct l2cap_options opts;	struct l2cap_conninfo cinfo;	int len, err = 0; 	if (get_user(len, optlen))		return -EFAULT;	lock_sock(sk);	switch (optname) {	case L2CAP_OPTIONS:		opts.imtu     = l2cap_pi(sk)->imtu;		opts.omtu     = l2cap_pi(sk)->omtu;		opts.flush_to = l2cap_pi(sk)->flush_to;		len = min_t(unsigned int, len, sizeof(opts));		if (copy_to_user(optval, (char *)&opts, len))			err = -EFAULT;		break;	case L2CAP_LM:		if (put_user(l2cap_pi(sk)->link_mode, (u32 __user *)optval))			err = -EFAULT;		break;	case L2CAP_CONNINFO:		if (sk->sk_state != BT_CONNECTED) {			err = -ENOTCONN;			break;		}		cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle;		len = min_t(unsigned int, len, sizeof(cinfo));		if (copy_to_user(optval, (char *)&cinfo, len))			err = -EFAULT;		break;	default:		err = -ENOPROTOOPT;		break;	}	release_sock(sk);	return err;}static int l2cap_sock_shutdown(struct socket *sock, int how){	struct sock *sk = sock->sk;	int err = 0;	BT_DBG("sock %p, sk %p", sock, sk);	if (!sk)		return 0;	lock_sock(sk);	if (!sk->sk_shutdown) {		sk->sk_shutdown = SHUTDOWN_MASK;		l2cap_sock_clear_timer(sk);		__l2cap_sock_close(sk, 0);		if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)			err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime);	}	release_sock(sk);	return err;}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;}/* ---- L2CAP channels ---- */static struct sock *__l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, u16 cid){	struct sock *s;	for (s = l->head; s; s = l2cap_pi(s)->next_c) {		if (l2cap_pi(s)->dcid == cid)			break;	}	return s;}static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid){	struct sock *s;	for (s = l->head; s; s = l2cap_pi(s)->next_c) {		if (l2cap_pi(s)->scid == cid)			break;	}	return s;}/* Find channel with given SCID. * Returns locked socket */static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid){	struct sock *s;	read_lock(&l->lock);	s = __l2cap_get_chan_by_scid(l, cid);	if (s) bh_lock_sock(s);	read_unlock(&l->lock);	return s;}static u16 l2cap_alloc_cid(struct l2cap_chan_list *l){	u16 cid = 0x0040;	for (; cid < 0xffff; cid++) {		if(!__l2cap_get_chan_by_scid(l, cid))			return cid;	}	return 0;}static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk){	sock_hold(sk);	if (l->head)		l2cap_pi(l->head)->prev_c = sk;	l2cap_pi(sk)->next_c = l->head;	l2cap_pi(sk)->prev_c = NULL;	l->head = sk;}static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk){	struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c;	write_lock(&l->lock);	if (sk == l->head)		l->head = next;	if (next)		l2cap_pi(next)->prev_c = prev;	if (prev)		l2cap_pi(prev)->next_c = next;	write_unlock(&l->lock);	__sock_put(sk);}static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent){	struct l2cap_chan_list *l = &conn->chan_list;	BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);	l2cap_pi(sk)->conn = conn;	if (sk->sk_type == SOCK_SEQPACKET) {		/* Alloc CID for connection-oriented socket */		l2cap_pi(sk)->scid = l2cap_alloc_cid(l);	} else if (sk->sk_type == SOCK_DGRAM) {		/* Connectionless socket */		l2cap_pi(sk)->scid = 0x0002;		l2cap_pi(sk)->dcid = 0x0002;		l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;	} else {		/* Raw socket can send/recv signalling messages only */		l2cap_pi(sk)->scid = 0x0001;		l2cap_pi(sk)->dcid = 0x0001;		l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;	}	__l2cap_chan_link(l, sk);	if (parent)		bt_accept_enqueue(parent, sk);}/* Delete channel.  * Must be called on the locked socket. */static void l2cap_chan_del(struct sock *sk, int err){	struct l2cap_conn *conn = l2cap_pi(sk)->conn;	struct sock *parent = bt_sk(sk)->parent;	l2cap_sock_clear_timer(sk);	BT_DBG("sk %p, conn %p, err %d", sk, conn, err);	if (conn) { 		/* Unlink from channel list */		l2cap_chan_unlink(&conn->chan_list, sk);		l2cap_pi(sk)->conn = NULL;		hci_conn_put(conn->hcon);	}	sk->sk_state  = BT_CLOSED;	sk->sk_zapped = 1;	if (err)		sk->sk_err = err;	if (parent)		parent->sk_data_ready(parent, 0);	else		sk->sk_state_change(sk);}static void l2cap_conn_ready(struct l2cap_conn *conn){	struct l2cap_chan_list *l = &conn->chan_list;	struct sock *sk;	BT_DBG("conn %p", conn);	read_lock(&l->lock);	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {		bh_lock_sock(sk);		if (sk->sk_type != SOCK_SEQPACKET) {			l2cap_sock_clear_timer(sk);			sk->sk_state = BT_CONNECTED;			sk->sk_state_change(sk);		} else if (sk->sk_state == BT_CONNECT) {			struct l2cap_conn_req req;			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);			req.psm  = l2cap_pi(sk)->psm;			l2cap_send_req(conn, L2CAP_CONN_REQ, sizeof(req), &req);		}		bh_unlock_sock(sk);	}	read_unlock(&l->lock);}static void l2cap_chan_ready(struct sock *sk){	struct sock *parent = bt_sk(sk)->parent;	BT_DBG("sk %p, parent %p", sk, parent);	l2cap_pi(sk)->conf_state = 0;	l2cap_sock_clear_timer(sk);	if (!parent) {		/* Outgoing channel.		 * Wake up socket sleeping on connect.		 */		sk->sk_state = BT_CONNECTED;		sk->sk_state_change(sk);	} else {		/* Incoming channel.		 * Wake up socket sleeping on accept.		 */		parent->sk_data_ready(parent, 0);	}}/* Copy frame to all raw sockets on that connection */static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb){	struct l2cap_chan_list *l = &conn->chan_list;	struct sk_buff *nskb;	struct sock * sk;	BT_DBG("conn %p", conn);	read_lock(&l->lock);	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {		if (sk->sk_type != SOCK_RAW)			continue;		/* Don't send frame to the socket it came from */		if (skb->sk == sk)			continue;		if (!(nskb = skb_clone(skb, GFP_ATOMIC)))			continue;		if (sock_queue_rcv_skb(sk, nskb))			kfree_skb(nskb);	}	read_unlock(&l->lock);}/* ---- L2CAP signalling commands ---- */static inline u8 l2cap_get_ident(struct l2cap_conn *conn){	u8 id;	/* Get next available identificator.	 *    1 - 199 are used by kernel.	 *  200 - 254 are used by utilities like l2ping, etc 	 */	spin_lock(&conn->lock);	if (++conn->tx_ident > 199)		conn->tx_ident = 1;	id = conn->tx_ident;	spin_unlock(&conn->lock);	return id;}static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,				u8 code, u8 ident, u16 dlen, void *data){	struct sk_buff *skb, **frag;	struct l2cap_cmd_hdr *cmd;	struct l2cap_hdr *lh;	int len, count;	BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d", conn, code, ident, dlen);	len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen;	count = min_t(unsigned int, conn->mtu, len);

⌨️ 快捷键说明

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