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

📄 l2cap.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	.name		= "L2CAP",	.owner		= THIS_MODULE,	.obj_size	= sizeof(struct l2cap_pinfo)};static 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;	l2cap_sock_init_timer(sk);	bt_sock_link(&l2cap_sk_list, sk);	return sk;}static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol){	struct sock *sk;	BT_DBG("sock %p", sock);	sock->state = SS_UNCONNECTED;	if (sock->type != SOCK_SEQPACKET &&			sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)		return -ESOCKTNOSUPPORT;	if (sock->type == SOCK_RAW && !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;}static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len){	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;	struct sock *sk = sock->sk;	int err = 0;	BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);	if (!addr || addr->sa_family != AF_BLUETOOTH)		return -EINVAL;	lock_sock(sk);	if (sk->sk_state != BT_OPEN) {		err = -EBADFD;		goto done;	}	if (la->l2_psm && btohs(la->l2_psm) < 0x1001 &&				!capable(CAP_NET_BIND_SERVICE)) {		err = -EACCES;		goto done;	}	write_lock_bh(&l2cap_sk_list.lock);	if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) {		err = -EADDRINUSE;	} else {		/* Save source address */		bacpy(&bt_sk(sk)->src, &la->l2_bdaddr);		l2cap_pi(sk)->psm   = la->l2_psm;		l2cap_pi(sk)->sport = la->l2_psm;		sk->sk_state = BT_BOUND;	}	write_unlock_bh(&l2cap_sk_list.lock);done:	release_sock(sk);	return err;}static int l2cap_do_connect(struct sock *sk){	bdaddr_t *src = &bt_sk(sk)->src;	bdaddr_t *dst = &bt_sk(sk)->dst;	struct l2cap_conn *conn;	struct hci_conn *hcon;	struct hci_dev *hdev;	int err = 0;	BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);	if (!(hdev = hci_get_route(dst, src)))		return -EHOSTUNREACH;	hci_dev_lock_bh(hdev);	err = -ENOMEM;	hcon = hci_connect(hdev, ACL_LINK, dst);	if (!hcon)		goto done;	conn = l2cap_conn_add(hcon, 0);	if (!conn) {		hci_conn_put(hcon);		goto done;	}	err = 0;	/* Update source addr of the socket */	bacpy(src, conn->src);	l2cap_chan_add(conn, sk, NULL);	sk->sk_state = BT_CONNECT;	l2cap_sock_set_timer(sk, sk->sk_sndtimeo);	if (hcon->state == BT_CONNECTED) {		if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)) {			l2cap_conn_ready(conn);			goto done;		}		if (sk->sk_type == SOCK_SEQPACKET) {			struct l2cap_conn_req req;			l2cap_pi(sk)->ident = l2cap_get_ident(conn);			req.scid = cpu_to_le16(l2cap_pi(sk)->scid);			req.psm  = l2cap_pi(sk)->psm;			l2cap_send_cmd(conn, l2cap_pi(sk)->ident,					L2CAP_CONN_REQ, sizeof(req), &req);		} else {			l2cap_sock_clear_timer(sk);			sk->sk_state = BT_CONNECTED;		}	}done:	hci_dev_unlock_bh(hdev);	hci_dev_put(hdev);	return err;}static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags){	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;	struct sock *sk = sock->sk;	int err = 0;	lock_sock(sk);	BT_DBG("sk %p", sk);	if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {		err = -EINVAL;		goto done;	}	if (sk->sk_type == SOCK_SEQPACKET && !la->l2_psm) {		err = -EINVAL;		goto done;	}	switch(sk->sk_state) {	case BT_CONNECT:	case BT_CONNECT2:	case BT_CONFIG:		/* Already connecting */		goto wait;	case BT_CONNECTED:		/* Already connected */		goto done;	case BT_OPEN:	case BT_BOUND:		/* Can connect */		break;	default:		err = -EBADFD;		goto done;	}	/* Set destination address and psm */	bacpy(&bt_sk(sk)->dst, &la->l2_bdaddr);	l2cap_pi(sk)->psm = la->l2_psm;	if ((err = l2cap_do_connect(sk)))		goto done;wait:	err = bt_sock_wait_state(sk, BT_CONNECTED,			sock_sndtimeo(sk, flags & O_NONBLOCK));done:	release_sock(sk);	return err;}static int l2cap_sock_listen(struct socket *sock, int backlog){	struct sock *sk = sock->sk;	int err = 0;	BT_DBG("sk %p backlog %d", sk, backlog);	lock_sock(sk);	if (sk->sk_state != BT_BOUND || sock->type != SOCK_SEQPACKET) {		err = -EBADFD;		goto done;	}	if (!l2cap_pi(sk)->psm) {		bdaddr_t *src = &bt_sk(sk)->src;		u16 psm;		err = -EINVAL;		write_lock_bh(&l2cap_sk_list.lock);		for (psm = 0x1001; psm < 0x1100; psm += 2)			if (!__l2cap_get_sock_by_addr(htobs(psm), src)) {				l2cap_pi(sk)->psm   = htobs(psm);				l2cap_pi(sk)->sport = htobs(psm);				err = 0;				break;			}		write_unlock_bh(&l2cap_sk_list.lock);		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_nested(sk, SINGLE_DEPTH_NESTING);	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_nested(sk, SINGLE_DEPTH_NESTING);		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, (__le16 *) 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);	err = sock_error(sk);	if (err)		return err;	if (msg->msg_flags & MSG_OOB)		return -EOPNOTSUPP;	/* Check outgoing MTU */	if (sk->sk_type != SOCK_RAW && 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:		opts.imtu     = l2cap_pi(sk)->imtu;		opts.omtu     = l2cap_pi(sk)->omtu;		opts.flush_to = l2cap_pi(sk)->flush_to;		opts.mode     = L2CAP_MODE_BASIC;		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;	BT_DBG("sk %p", sk);	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;		opts.mode     = L2CAP_MODE_BASIC;		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;		memcpy(cinfo.dev_class, l2cap_pi(sk)->conn->hcon->dev_class, 3);		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;}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 */

⌨️ 快捷键说明

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