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

📄 l2cap.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	if (type == L2CAP_IT_FEAT_MASK) {		u8 buf[8];		struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;		rsp->type   = cpu_to_le16(L2CAP_IT_FEAT_MASK);		rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);		put_unaligned(cpu_to_le32(l2cap_feat_mask), (__le32 *) rsp->data);		l2cap_send_cmd(conn, cmd->ident,					L2CAP_INFO_RSP, sizeof(buf), buf);	} else {		struct l2cap_info_rsp rsp;		rsp.type   = cpu_to_le16(type);		rsp.result = cpu_to_le16(L2CAP_IR_NOTSUPP);		l2cap_send_cmd(conn, cmd->ident,					L2CAP_INFO_RSP, sizeof(rsp), &rsp);	}	return 0;}static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data){	struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data;	u16 type, result;	type   = __le16_to_cpu(rsp->type);	result = __le16_to_cpu(rsp->result);	BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);	conn->info_ident = 0;	del_timer(&conn->info_timer);	if (type == L2CAP_IT_FEAT_MASK)		conn->feat_mask = __le32_to_cpu(get_unaligned((__le32 *) rsp->data));	l2cap_conn_start(conn);	return 0;}static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb){	u8 *data = skb->data;	int len = skb->len;	struct l2cap_cmd_hdr cmd;	int err = 0;	l2cap_raw_recv(conn, skb);	while (len >= L2CAP_CMD_HDR_SIZE) {		u16 cmd_len;		memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);		data += L2CAP_CMD_HDR_SIZE;		len  -= L2CAP_CMD_HDR_SIZE;		cmd_len = le16_to_cpu(cmd.len);		BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len, cmd.ident);		if (cmd_len > len || !cmd.ident) {			BT_DBG("corrupted command");			break;		}		switch (cmd.code) {		case L2CAP_COMMAND_REJ:			l2cap_command_rej(conn, &cmd, data);			break;		case L2CAP_CONN_REQ:			err = l2cap_connect_req(conn, &cmd, data);			break;		case L2CAP_CONN_RSP:			err = l2cap_connect_rsp(conn, &cmd, data);			break;		case L2CAP_CONF_REQ:			err = l2cap_config_req(conn, &cmd, cmd_len, data);			break;		case L2CAP_CONF_RSP:			err = l2cap_config_rsp(conn, &cmd, data);			break;		case L2CAP_DISCONN_REQ:			err = l2cap_disconnect_req(conn, &cmd, data);			break;		case L2CAP_DISCONN_RSP:			err = l2cap_disconnect_rsp(conn, &cmd, data);			break;		case L2CAP_ECHO_REQ:			l2cap_send_cmd(conn, cmd.ident, L2CAP_ECHO_RSP, cmd_len, data);			break;		case L2CAP_ECHO_RSP:			break;		case L2CAP_INFO_REQ:			err = l2cap_information_req(conn, &cmd, data);			break;		case L2CAP_INFO_RSP:			err = l2cap_information_rsp(conn, &cmd, data);			break;		default:			BT_ERR("Unknown signaling command 0x%2.2x", cmd.code);			err = -EINVAL;			break;		}		if (err) {			struct l2cap_cmd_rej rej;			BT_DBG("error %d", err);			/* FIXME: Map err to a valid reason */			rej.reason = cpu_to_le16(0);			l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);		}		data += cmd_len;		len  -= cmd_len;	}	kfree_skb(skb);}static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb){	struct sock *sk;	sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);	if (!sk) {		BT_DBG("unknown cid 0x%4.4x", cid);		goto drop;	}	BT_DBG("sk %p, len %d", sk, skb->len);	if (sk->sk_state != BT_CONNECTED)		goto drop;	if (l2cap_pi(sk)->imtu < skb->len)		goto drop;	/* If socket recv buffers overflows we drop data here	 * which is *bad* because L2CAP has to be reliable.	 * But we don't have any other choice. L2CAP doesn't	 * provide flow control mechanism. */	if (!sock_queue_rcv_skb(sk, skb))		goto done;drop:	kfree_skb(skb);done:	if (sk)		bh_unlock_sock(sk);	return 0;}static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb){	struct sock *sk;	sk = l2cap_get_sock_by_psm(0, psm, conn->src);	if (!sk)		goto drop;	BT_DBG("sk %p, len %d", sk, skb->len);	if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED)		goto drop;	if (l2cap_pi(sk)->imtu < skb->len)		goto drop;	if (!sock_queue_rcv_skb(sk, skb))		goto done;drop:	kfree_skb(skb);done:	if (sk) bh_unlock_sock(sk);	return 0;}static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb){	struct l2cap_hdr *lh = (void *) skb->data;	u16 cid, len;	__le16 psm;	skb_pull(skb, L2CAP_HDR_SIZE);	cid = __le16_to_cpu(lh->cid);	len = __le16_to_cpu(lh->len);	BT_DBG("len %d, cid 0x%4.4x", len, cid);	switch (cid) {	case 0x0001:		l2cap_sig_channel(conn, skb);		break;	case 0x0002:		psm = get_unaligned((__le16 *) skb->data);		skb_pull(skb, 2);		l2cap_conless_channel(conn, psm, skb);		break;	default:		l2cap_data_channel(conn, cid, skb);		break;	}}/* ---- L2CAP interface with lower layer (HCI) ---- */static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type){	int exact = 0, lm1 = 0, lm2 = 0;	register struct sock *sk;	struct hlist_node *node;	if (type != ACL_LINK)		return 0;	BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));	/* Find listening sockets and check their link_mode */	read_lock(&l2cap_sk_list.lock);	sk_for_each(sk, node, &l2cap_sk_list.head) {		if (sk->sk_state != BT_LISTEN)			continue;		if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {			lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);			exact++;		} else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))			lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);	}	read_unlock(&l2cap_sk_list.lock);	return exact ? lm1 : lm2;}static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status){	struct l2cap_conn *conn;	BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);	if (hcon->type != ACL_LINK)		return 0;	if (!status) {		conn = l2cap_conn_add(hcon, status);		if (conn)			l2cap_conn_ready(conn);	} else		l2cap_conn_del(hcon, bt_err(status));	return 0;}static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason){	BT_DBG("hcon %p reason %d", hcon, reason);	if (hcon->type != ACL_LINK)		return 0;	l2cap_conn_del(hcon, bt_err(reason));	return 0;}static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status){	struct l2cap_chan_list *l;	struct l2cap_conn *conn = conn = hcon->l2cap_data;	struct l2cap_conn_rsp rsp;	struct sock *sk;	int result;	if (!conn)		return 0;	l = &conn->chan_list;	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_state != BT_CONNECT2 ||				(l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||				(l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)) {			bh_unlock_sock(sk);			continue;		}		if (!status) {			sk->sk_state = BT_CONFIG;			result = 0;		} else {			sk->sk_state = BT_DISCONN;			l2cap_sock_set_timer(sk, HZ/10);			result = L2CAP_CR_SEC_BLOCK;		}		rsp.scid   = cpu_to_le16(l2cap_pi(sk)->dcid);		rsp.dcid   = cpu_to_le16(l2cap_pi(sk)->scid);		rsp.result = cpu_to_le16(result);		rsp.status = cpu_to_le16(0);		l2cap_send_cmd(conn, l2cap_pi(sk)->ident,				L2CAP_CONN_RSP, sizeof(rsp), &rsp);		bh_unlock_sock(sk);	}	read_unlock(&l->lock);	return 0;}static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status){	struct l2cap_chan_list *l;	struct l2cap_conn *conn = hcon->l2cap_data;	struct l2cap_conn_rsp rsp;	struct sock *sk;	int result;	if (!conn)		return 0;	l = &conn->chan_list;	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_state != BT_CONNECT2) {			bh_unlock_sock(sk);			continue;		}		if (!status) {			sk->sk_state = BT_CONFIG;			result = 0;		} else {			sk->sk_state = BT_DISCONN;			l2cap_sock_set_timer(sk, HZ/10);			result = L2CAP_CR_SEC_BLOCK;		}		rsp.scid   = cpu_to_le16(l2cap_pi(sk)->dcid);		rsp.dcid   = cpu_to_le16(l2cap_pi(sk)->scid);		rsp.result = cpu_to_le16(result);		rsp.status = cpu_to_le16(0);		l2cap_send_cmd(conn, l2cap_pi(sk)->ident,				L2CAP_CONN_RSP, sizeof(rsp), &rsp);		if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)			hci_conn_change_link_key(hcon);		bh_unlock_sock(sk);	}	read_unlock(&l->lock);	return 0;}static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags){	struct l2cap_conn *conn = hcon->l2cap_data;	if (!conn && !(conn = l2cap_conn_add(hcon, 0)))		goto drop;	BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);	if (flags & ACL_START) {		struct l2cap_hdr *hdr;		int len;		if (conn->rx_len) {			BT_ERR("Unexpected start frame (len %d)", skb->len);			kfree_skb(conn->rx_skb);			conn->rx_skb = NULL;			conn->rx_len = 0;			l2cap_conn_unreliable(conn, ECOMM);		}		if (skb->len < 2) {			BT_ERR("Frame is too short (len %d)", skb->len);			l2cap_conn_unreliable(conn, ECOMM);			goto drop;		}		hdr = (struct l2cap_hdr *) skb->data;		len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;		if (len == skb->len) {			/* Complete frame received */			l2cap_recv_frame(conn, skb);			return 0;		}		BT_DBG("Start: total len %d, frag len %d", len, skb->len);		if (skb->len > len) {			BT_ERR("Frame is too long (len %d, expected len %d)",				skb->len, len);			l2cap_conn_unreliable(conn, ECOMM);			goto drop;		}		/* Allocate skb for the complete frame (with header) */		if (!(conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC)))			goto drop;		skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),			      skb->len);		conn->rx_len = len - skb->len;	} else {		BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);		if (!conn->rx_len) {			BT_ERR("Unexpected continuation frame (len %d)", skb->len);			l2cap_conn_unreliable(conn, ECOMM);			goto drop;		}		if (skb->len > conn->rx_len) {			BT_ERR("Fragment is too long (len %d, expected %d)",					skb->len, conn->rx_len);			kfree_skb(conn->rx_skb);			conn->rx_skb = NULL;			conn->rx_len = 0;			l2cap_conn_unreliable(conn, ECOMM);			goto drop;		}		skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),			      skb->len);		conn->rx_len -= skb->len;		if (!conn->rx_len) {			/* Complete frame received */			l2cap_recv_frame(conn, conn->rx_skb);			conn->rx_skb = NULL;		}	}drop:	kfree_skb(skb);	return 0;}static ssize_t l2cap_sysfs_show(struct class *dev, char *buf){	struct sock *sk;	struct hlist_node *node;	char *str = buf;	read_lock_bh(&l2cap_sk_list.lock);	sk_for_each(sk, node, &l2cap_sk_list.head) {		struct l2cap_pinfo *pi = l2cap_pi(sk);		str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n",				batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),				sk->sk_state, btohs(pi->psm), pi->scid, pi->dcid,				pi->imtu, pi->omtu, pi->link_mode);	}	read_unlock_bh(&l2cap_sk_list.lock);	return (str - buf);}static CLASS_ATTR(l2cap, S_IRUGO, l2cap_sysfs_show, NULL);static 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	= bt_sock_recvmsg,	.poll		= bt_sock_poll,	.mmap		= sock_no_mmap,	.socketpair	= sock_no_socketpair,	.ioctl		= sock_no_ioctl,	.shutdown	= l2cap_sock_shutdown,	.setsockopt	= l2cap_sock_setsockopt,	.getsockopt	= l2cap_sock_getsockopt};static struct net_proto_family l2cap_sock_family_ops = {	.family	= PF_BLUETOOTH,	.owner	= THIS_MODULE,	.create	= l2cap_sock_create,};static struct hci_proto l2cap_hci_proto = {	.name		= "L2CAP",	.id		= HCI_PROTO_L2CAP,	.connect_ind	= l2cap_connect_ind,	.connect_cfm	= l2cap_connect_cfm,	.disconn_ind	= l2cap_disconn_ind,	.auth_cfm	= l2cap_auth_cfm,	.encrypt_cfm	= l2cap_encrypt_cfm,	.recv_acldata	= l2cap_recv_acldata};static int __init l2cap_init(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) {		BT_ERR("L2CAP socket registration failed");		goto error;	}	err = hci_register_proto(&l2cap_hci_proto);	if (err < 0) {		BT_ERR("L2CAP protocol registration failed");		bt_sock_unregister(BTPROTO_L2CAP);		goto error;	}	if (class_create_file(bt_class, &class_attr_l2cap) < 0)		BT_ERR("Failed to create L2CAP info file");	BT_INFO("L2CAP ver %s", VERSION);	BT_INFO("L2CAP socket layer initialized");	return 0;error:	proto_unregister(&l2cap_proto);	return err;}static void __exit l2cap_exit(void){	class_remove_file(bt_class, &class_attr_l2cap);	if (bt_sock_unregister(BTPROTO_L2CAP) < 0)		BT_ERR("L2CAP socket unregistration failed");	if (hci_unregister_proto(&l2cap_hci_proto) < 0)		BT_ERR("L2CAP protocol unregistration failed");	proto_unregister(&l2cap_proto);}void l2cap_load(void){	/* Dummy function to trigger automatic L2CAP module loading by	 * other modules that use L2CAP sockets but don't use any other	 * symbols from it. */	return;}EXPORT_SYMBOL(l2cap_load);module_init(l2cap_init);module_exit(l2cap_exit);MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION);MODULE_VERSION(VERSION);MODULE_LICENSE("GPL");MODULE_ALIAS("bt-proto-0");

⌨️ 快捷键说明

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