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

📄 l2cap.c

📁 Linux嵌入式平台蓝牙协议栈软件
💻 C
📖 第 1 页 / 共 4 页
字号:
	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 int l2cap_send_req(struct l2cap_conn *conn, u8 code, u16 len, void *data){	u8 ident = l2cap_get_ident(conn);	struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);	BT_DBG("code 0x%2.2x", code);	if (!skb)		return -ENOMEM;	return hci_send_acl(conn->hcon, skb, 0);}static int l2cap_send_rsp(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data){	struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);	BT_DBG("code 0x%2.2x", code);	if (!skb)		return -ENOMEM;	return hci_send_acl(conn->hcon, skb, 0);}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(*((u16 *)opt->val));		break;	case 4:		*val = __le32_to_cpu(*((u32 *)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 inline void l2cap_parse_conf_req(struct sock *sk, void *data, int len){	int type, hint, olen; 	unsigned long val;	void *ptr = data;	BT_DBG("sk %p len %d", sk, len);	while (len >= L2CAP_CONF_OPT_SIZE) {		len -= l2cap_get_conf_opt(&ptr, &type, &olen, &val);		hint  = type & 0x80;		type &= 0x7f;		switch (type) {		case L2CAP_CONF_MTU:			l2cap_pi(sk)->conf_mtu = val;			break;		case L2CAP_CONF_FLUSH_TO:			l2cap_pi(sk)->flush_to = val;			break;		case L2CAP_CONF_QOS:			break;		default:			if (hint)				break;			/* FIXME: Reject unknown option */			break;		}	}}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:		*((u16 *) opt->val) = __cpu_to_le16(val);		break;	case 4:		*((u32 *) 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 inline int l2cap_conf_output(struct sock *sk, void **ptr){	struct l2cap_pinfo *pi = l2cap_pi(sk);	int result = 0;	/* Configure output options and let the other side know	 * which ones we don't like. */	if (pi->conf_mtu < pi->omtu) {		l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, pi->omtu);		result = L2CAP_CONF_UNACCEPT;	} else {		pi->omtu = pi->conf_mtu;	}	BT_DBG("sk %p result %d", sk, result);	return result;}static int l2cap_build_conf_rsp(struct sock *sk, void *data, int *result){	struct l2cap_conf_rsp *rsp = data;	void *ptr = rsp->data;	u16 flags = 0;	BT_DBG("sk %p complete %d", sk, result ? 1 : 0);	if (result)		*result = l2cap_conf_output(sk, &ptr);	else		flags = 0x0001;	rsp->scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);	rsp->result = __cpu_to_le16(result ? *result : 0);	rsp->flags  = __cpu_to_le16(flags);	return ptr - data;}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);	u16 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 (parent->sk_ack_backlog > parent->sk_max_ack_backlog) {		BT_DBG("backlog full %d", parent->sk_ack_backlog); 		goto response;	}	sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC);	if (!sk)		goto response;	write_lock(&list->lock);	/* Check if we already have channel with that dcid */	if (__l2cap_get_chan_by_dcid(list, scid)) {		write_unlock(&list->lock);		sk->sk_zapped = 1;		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) {		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(&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_rsp(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 (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))		return -ENOENT;	switch (result) {	case L2CAP_CR_SUCCESS:		sk->sk_state = BT_CONFIG;		l2cap_pi(sk)->dcid = dcid;		l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;		l2cap_send_req(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, u8 *data){	struct l2cap_conf_req *req = (struct l2cap_conf_req *) data;	u16 dcid, flags;	u8 rsp[64];	struct sock *sk;	int result;	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;	l2cap_parse_conf_req(sk, req->data, cmd->len - sizeof(*req));	if (flags & 0x0001) {		/* Incomplete config. Send empty response. */		l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp);		goto unlock;	}	/* Complete config. */	l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, &result), rsp);	if (result)		goto unlock;	/* Output config done */	l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE;	if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {		sk->sk_state = BT_CONNECTED;		l2cap_chan_ready(sk);	} else if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {		u8 req[64];		l2cap_send_req(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;	int err = 0;	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 -ENOENT;	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_req(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_req(conn, L2CAP_DISCONN_REQ, sizeof(req), &req);		}		goto done;	}	if (flags & 0x01)		goto done;	/* Input config 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 err;}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_rsp(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_info_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data){	struct l2cap_info_req *req = (struct l2cap_info_req *) data;	struct l2cap_info_rsp rsp;	u16 type;	type = __le16_to_cpu(req->type);	BT_DBG("type 0x%4.4x", type);	rsp.type   = __cpu_to_le16(type);	rsp.result = __cpu_to_le16(L2CAP_IR_NOTSUPP);	l2cap_send_rsp(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), &rsp);	return 0;}static inline int l2cap_info_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);	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) {		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:			/* FIXME: We should process this */

⌨️ 快捷键说明

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