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

📄 l2cap.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
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 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);	skb = bt_skb_alloc(count, GFP_ATOMIC);	if (!skb)		return NULL;	lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);	lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen);	lh->cid = cpu_to_le16(0x0001);	cmd = (struct l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);	cmd->code  = code;	cmd->ident = ident;	cmd->len   = cpu_to_le16(dlen);	if (dlen) {		count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE;		memcpy(skb_put(skb, count), data, count);		data += count;	}	len -= skb->len;	/* Continuation fragments (no L2CAP header) */	frag = &skb_shinfo(skb)->frag_list;	while (len) {		count = min_t(unsigned int, conn->mtu, len);		*frag = bt_skb_alloc(count, GFP_ATOMIC);		if (!*frag)			goto fail;		memcpy(skb_put(*frag, count), data, count);		len  -= count;		data += count;		frag = &(*frag)->next;	}	return skb;fail:	kfree_skb(skb);	return NULL;}static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val){	struct l2cap_conf_opt *opt = *ptr;	int len;	len = L2CAP_CONF_OPT_SIZE + opt->len;	*ptr += len;	*type = opt->type;	*olen = opt->len;	switch (opt->len) {	case 1:		*val = *((u8 *) opt->val);		break;	case 2:		*val = __le16_to_cpu(*((__le16 *) opt->val));		break;	case 4:		*val = __le32_to_cpu(*((__le32 *) opt->val));		break;	default:		*val = (unsigned long) opt->val;		break;	}	BT_DBG("type 0x%2.2x len %d val 0x%lx", *type, opt->len, *val);	return len;}static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val){	struct l2cap_conf_opt *opt = *ptr;	BT_DBG("type 0x%2.2x len %d val 0x%lx", type, len, val);	opt->type = type;	opt->len  = len;	switch (len) {	case 1:		*((u8 *) opt->val)  = val;		break;	case 2:		*((__le16 *) opt->val) = cpu_to_le16(val);		break;	case 4:		*((__le32 *) opt->val) = cpu_to_le32(val);		break;	default:		memcpy(opt->val, (void *) val, len);		break;	}	*ptr += L2CAP_CONF_OPT_SIZE + len;}static int l2cap_build_conf_req(struct sock *sk, void *data){	struct l2cap_pinfo *pi = l2cap_pi(sk);	struct l2cap_conf_req *req = data;	void *ptr = req->data;	BT_DBG("sk %p", sk);	if (pi->imtu != L2CAP_DEFAULT_MTU)		l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);	/* FIXME: Need actual value of the flush timeout */	//if (flush_to != L2CAP_DEFAULT_FLUSH_TO)	//   l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);	req->dcid  = cpu_to_le16(pi->dcid);	req->flags = cpu_to_le16(0);	return ptr - data;}static int l2cap_parse_conf_req(struct sock *sk, void *data){	struct l2cap_pinfo *pi = l2cap_pi(sk);	struct l2cap_conf_rsp *rsp = data;	void *ptr = rsp->data;	void *req = pi->conf_req;	int len = pi->conf_len;	int type, hint, olen;	unsigned long val;	struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };	u16 mtu = L2CAP_DEFAULT_MTU;	u16 result = L2CAP_CONF_SUCCESS;	BT_DBG("sk %p", sk);	while (len >= L2CAP_CONF_OPT_SIZE) {		len -= l2cap_get_conf_opt(&req, &type, &olen, &val);		hint  = type & 0x80;		type &= 0x7f;		switch (type) {		case L2CAP_CONF_MTU:			mtu = val;			break;		case L2CAP_CONF_FLUSH_TO:			pi->flush_to = val;			break;		case L2CAP_CONF_QOS:			break;		case L2CAP_CONF_RFC:			if (olen == sizeof(rfc))				memcpy(&rfc, (void *) val, olen);			break;		default:			if (hint)				break;			result = L2CAP_CONF_UNKNOWN;			*((u8 *) ptr++) = type;			break;		}	}	if (result == L2CAP_CONF_SUCCESS) {		/* Configure output options and let the other side know		 * which ones we don't like. */		if (rfc.mode == L2CAP_MODE_BASIC) {			if (mtu < pi->omtu)				result = L2CAP_CONF_UNACCEPT;			else {				pi->omtu = mtu;				pi->conf_state |= L2CAP_CONF_OUTPUT_DONE;			}			l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);		} else {			result = L2CAP_CONF_UNACCEPT;			memset(&rfc, 0, sizeof(rfc));			rfc.mode = L2CAP_MODE_BASIC;			l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,						sizeof(rfc), (unsigned long) &rfc);		}	}	rsp->scid   = cpu_to_le16(pi->dcid);	rsp->result = cpu_to_le16(result);	rsp->flags  = cpu_to_le16(0x0000);	return ptr - data;}static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 flags){	struct l2cap_conf_rsp *rsp = data;	void *ptr = rsp->data;	BT_DBG("sk %p", sk);	rsp->scid   = cpu_to_le16(l2cap_pi(sk)->dcid);	rsp->result = cpu_to_le16(result);	rsp->flags  = cpu_to_le16(flags);	return ptr - data;}static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data){	struct l2cap_cmd_rej *rej = (struct l2cap_cmd_rej *) data;	if (rej->reason != 0x0000)		return 0;	if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&					cmd->ident == conn->info_ident) {		conn->info_ident = 0;		del_timer(&conn->info_timer);		l2cap_conn_start(conn);	}	return 0;}static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data){	struct l2cap_chan_list *list = &conn->chan_list;	struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;	struct l2cap_conn_rsp rsp;	struct sock *sk, *parent;	int result = 0, status = 0;	u16 dcid = 0, scid = __le16_to_cpu(req->scid);	__le16 psm  = req->psm;	BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);	/* Check if we have socket listening on psm */	parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src);	if (!parent) {		result = L2CAP_CR_BAD_PSM;		goto sendresp;	}	result = L2CAP_CR_NO_MEM;	/* Check for backlog size */	if (sk_acceptq_is_full(parent)) {		BT_DBG("backlog full %d", parent->sk_ack_backlog);		goto response;	}	sk = l2cap_sock_alloc(parent->sk_net, NULL, BTPROTO_L2CAP, GFP_ATOMIC);	if (!sk)		goto response;	write_lock_bh(&list->lock);	/* Check if we already have channel with that dcid */	if (__l2cap_get_chan_by_dcid(list, scid)) {		write_unlock_bh(&list->lock);		sock_set_flag(sk, SOCK_ZAPPED);		l2cap_sock_kill(sk);		goto response;	}	hci_conn_hold(conn->hcon);	l2cap_sock_init(sk, parent);	bacpy(&bt_sk(sk)->src, conn->src);	bacpy(&bt_sk(sk)->dst, conn->dst);	l2cap_pi(sk)->psm  = psm;	l2cap_pi(sk)->dcid = scid;	__l2cap_chan_add(conn, sk, parent);	dcid = l2cap_pi(sk)->scid;	l2cap_sock_set_timer(sk, sk->sk_sndtimeo);	/* Service level security */	result = L2CAP_CR_PEND;	status = L2CAP_CS_AUTHEN_PEND;	sk->sk_state = BT_CONNECT2;	l2cap_pi(sk)->ident = cmd->ident;	if ((l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||			(l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)) {		if (!hci_conn_encrypt(conn->hcon))			goto done;	} else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) {		if (!hci_conn_auth(conn->hcon))			goto done;	}	sk->sk_state = BT_CONFIG;	result = status = 0;done:	write_unlock_bh(&list->lock);response:	bh_unlock_sock(parent);sendresp:	rsp.scid   = cpu_to_le16(scid);	rsp.dcid   = cpu_to_le16(dcid);	rsp.result = cpu_to_le16(result);	rsp.status = cpu_to_le16(status);	l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);	return 0;}static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data){	struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;	u16 scid, dcid, result, status;	struct sock *sk;	u8 req[128];	scid   = __le16_to_cpu(rsp->scid);	dcid   = __le16_to_cpu(rsp->dcid);	result = __le16_to_cpu(rsp->result);	status = __le16_to_cpu(rsp->status);	BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);	if (scid) {		if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))			return 0;	} else {		if (!(sk = l2cap_get_chan_by_ident(&conn->chan_list, cmd->ident)))			return 0;	}	switch (result) {	case L2CAP_CR_SUCCESS:		sk->sk_state = BT_CONFIG;		l2cap_pi(sk)->ident = 0;		l2cap_pi(sk)->dcid = dcid;		l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;		l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,					l2cap_build_conf_req(sk, req), req);		break;	case L2CAP_CR_PEND:		break;	default:		l2cap_chan_del(sk, ECONNREFUSED);		break;	}	bh_unlock_sock(sk);	return 0;}static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data){	struct l2cap_conf_req *req = (struct l2cap_conf_req *) data;	u16 dcid, flags;	u8 rsp[64];	struct sock *sk;	int len;	dcid  = __le16_to_cpu(req->dcid);	flags = __le16_to_cpu(req->flags);	BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))		return -ENOENT;	if (sk->sk_state == BT_DISCONN)		goto unlock;	/* Reject if config buffer is too small. */	len = cmd_len - sizeof(*req);	if (l2cap_pi(sk)->conf_len + len > sizeof(l2cap_pi(sk)->conf_req)) {		l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,				l2cap_build_conf_rsp(sk, rsp,					L2CAP_CONF_REJECT, flags), rsp);		goto unlock;	}	/* Store config. */	memcpy(l2cap_pi(sk)->conf_req + l2cap_pi(sk)->conf_len, req->data, len);	l2cap_pi(sk)->conf_len += len;	if (flags & 0x0001) {		/* Incomplete config. Send empty response. */		l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,				l2cap_build_conf_rsp(sk, rsp,					L2CAP_CONF_SUCCESS, 0x0001), rsp);		goto unlock;	}	/* Complete config. */	len = l2cap_parse_conf_req(sk, rsp);	if (len < 0)		goto unlock;	l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);	/* Reset config buffer. */	l2cap_pi(sk)->conf_len = 0;	if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE))		goto unlock;	if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {		sk->sk_state = BT_CONNECTED;		l2cap_chan_ready(sk);		goto unlock;	}	if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {		u8 req[64];		l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,					l2cap_build_conf_req(sk, req), req);	}unlock:	bh_unlock_sock(sk);	return 0;}static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data){	struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data;	u16 scid, flags, result;	struct sock *sk;	scid   = __le16_to_cpu(rsp->scid);	flags  = __le16_to_cpu(rsp->flags);	result = __le16_to_cpu(rsp->result);	BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result);	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))		return 0;	switch (result) {	case L2CAP_CONF_SUCCESS:		break;	case L2CAP_CONF_UNACCEPT:		if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) {			char req[128];			/* It does not make sense to adjust L2CAP parameters			 * that are currently defined in the spec. We simply			 * resend config request that we sent earlier. It is			 * stupid, but it helps qualification testing which			 * expects at least some response from us. */			l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,						l2cap_build_conf_req(sk, req), req);			goto done;		}	default:		sk->sk_state = BT_DISCONN;		sk->sk_err   = ECONNRESET;		l2cap_sock_set_timer(sk, HZ * 5);		{			struct l2cap_disconn_req req;			req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid);			req.scid = cpu_to_le16(l2cap_pi(sk)->scid);			l2cap_send_cmd(conn, l2cap_get_ident(conn),					L2CAP_DISCONN_REQ, sizeof(req), &req);		}		goto done;	}	if (flags & 0x01)		goto done;	l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE;	if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) {		sk->sk_state = BT_CONNECTED;		l2cap_chan_ready(sk);	}done:	bh_unlock_sock(sk);	return 0;}static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data){	struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data;	struct l2cap_disconn_rsp rsp;	u16 dcid, scid;	struct sock *sk;	scid = __le16_to_cpu(req->scid);	dcid = __le16_to_cpu(req->dcid);	BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))		return 0;	rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);	rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);	l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);	sk->sk_shutdown = SHUTDOWN_MASK;	l2cap_chan_del(sk, ECONNRESET);	bh_unlock_sock(sk);	l2cap_sock_kill(sk);	return 0;}static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data){	struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data;	u16 dcid, scid;	struct sock *sk;	scid = __le16_to_cpu(rsp->scid);	dcid = __le16_to_cpu(rsp->dcid);	BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))		return 0;	l2cap_chan_del(sk, 0);	bh_unlock_sock(sk);	l2cap_sock_kill(sk);	return 0;}static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data){	struct l2cap_info_req *req = (struct l2cap_info_req *) data;	u16 type;	type = __le16_to_cpu(req->type);	BT_DBG("type 0x%4.4x", type);

⌨️ 快捷键说明

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