1107120Sjulian/*
2107120Sjulian * ng_l2cap_ulpi.c
3139823Simp */
4139823Simp
5139823Simp/*-
6107120Sjulian * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
7107120Sjulian * All rights reserved.
8107120Sjulian *
9107120Sjulian * Redistribution and use in source and binary forms, with or without
10107120Sjulian * modification, are permitted provided that the following conditions
11107120Sjulian * are met:
12107120Sjulian * 1. Redistributions of source code must retain the above copyright
13107120Sjulian *    notice, this list of conditions and the following disclaimer.
14107120Sjulian * 2. Redistributions in binary form must reproduce the above copyright
15107120Sjulian *    notice, this list of conditions and the following disclaimer in the
16107120Sjulian *    documentation and/or other materials provided with the distribution.
17107120Sjulian *
18107120Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19107120Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20107120Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21107120Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22107120Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23107120Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24107120Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25107120Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26107120Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27107120Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28107120Sjulian * SUCH DAMAGE.
29107120Sjulian *
30114878Sjulian * $Id: ng_l2cap_ulpi.c,v 1.1 2002/11/24 19:47:06 max Exp $
31107120Sjulian * $FreeBSD$
32107120Sjulian */
33107120Sjulian
34107120Sjulian#include <sys/param.h>
35107120Sjulian#include <sys/systm.h>
36107120Sjulian#include <sys/kernel.h>
37107120Sjulian#include <sys/endian.h>
38107120Sjulian#include <sys/malloc.h>
39107120Sjulian#include <sys/mbuf.h>
40107120Sjulian#include <sys/queue.h>
41107120Sjulian#include <netgraph/ng_message.h>
42107120Sjulian#include <netgraph/netgraph.h>
43128688Semax#include <netgraph/bluetooth/include/ng_hci.h>
44128688Semax#include <netgraph/bluetooth/include/ng_l2cap.h>
45128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
46128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
47128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
48128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
49128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
50128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>
51107120Sjulian
52107120Sjulian/******************************************************************************
53107120Sjulian ******************************************************************************
54107120Sjulian **                 Upper Layer Protocol Interface module
55107120Sjulian ******************************************************************************
56107120Sjulian ******************************************************************************/
57107120Sjulian
58107120Sjulian/*
59107120Sjulian * Process L2CA_Connect request from the upper layer protocol.
60107120Sjulian */
61107120Sjulian
62107120Sjulianint
63107120Sjulianng_l2cap_l2ca_con_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
64107120Sjulian{
65107120Sjulian	ng_l2cap_l2ca_con_ip	*ip = NULL;
66107120Sjulian	ng_l2cap_con_p		 con = NULL;
67107120Sjulian	ng_l2cap_chan_p		 ch = NULL;
68107120Sjulian	ng_l2cap_cmd_p		 cmd = NULL;
69107120Sjulian	int			 error = 0;
70107120Sjulian
71107120Sjulian	/* Check message */
72107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
73107120Sjulian		NG_L2CAP_ALERT(
74107120Sjulian"%s: %s - invalid L2CA_Connect request message size, size=%d\n",
75107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
76107120Sjulian			msg->header.arglen);
77107120Sjulian		error = EMSGSIZE;
78107120Sjulian		goto out;
79107120Sjulian	}
80107120Sjulian
81107120Sjulian	ip = (ng_l2cap_l2ca_con_ip *)(msg->data);
82107120Sjulian
83107120Sjulian	/* Check if we have connection to the remote unit */
84281198Stakawata	con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr, ip->linktype);
85107120Sjulian	if (con == NULL) {
86107120Sjulian		/* Submit LP_ConnectReq to the lower layer */
87281198Stakawata		error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr,ip->linktype);
88107120Sjulian		if (error != 0) {
89107120Sjulian			NG_L2CAP_ERR(
90107120Sjulian"%s: %s - unable to send LP_ConnectReq message, error=%d\n",
91107120Sjulian				__func__, NG_NODE_NAME(l2cap->node), error);
92107120Sjulian			goto out;
93107120Sjulian		}
94107120Sjulian
95107120Sjulian		/* This should not fail */
96281198Stakawata		con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr, ip->linktype);
97107120Sjulian		KASSERT((con != NULL),
98107120Sjulian("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node)));
99107120Sjulian	}
100107120Sjulian
101107120Sjulian	/*
102107120Sjulian	 * Create new empty channel descriptor. In case of any failure do
103107120Sjulian	 * not touch connection descriptor.
104107120Sjulian	 */
105107120Sjulian
106281198Stakawata	ch = ng_l2cap_new_chan(l2cap, con, ip->psm, ip->idtype);
107107120Sjulian	if (ch == NULL) {
108107120Sjulian		error = ENOMEM;
109107120Sjulian		goto out;
110107120Sjulian	}
111107120Sjulian
112107120Sjulian	/* Now create L2CAP_ConnectReq command */
113107120Sjulian	cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(con),
114107120Sjulian			NG_L2CAP_CON_REQ, msg->header.token);
115107120Sjulian	if (cmd == NULL) {
116107120Sjulian		ng_l2cap_free_chan(ch);
117107120Sjulian		error = ENOMEM;
118107120Sjulian		goto out;
119107120Sjulian	}
120107120Sjulian
121107120Sjulian	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
122107120Sjulian		ng_l2cap_free_cmd(cmd);
123107120Sjulian		ng_l2cap_free_chan(ch);
124107120Sjulian		error = EIO;
125107120Sjulian		goto out;
126107120Sjulian	}
127107120Sjulian
128107120Sjulian	/* Create L2CAP command packet */
129281198Stakawata	if(ip->idtype == NG_L2CAP_L2CA_IDTYPE_ATT){
130281198Stakawata		_ng_l2cap_con_rsp(cmd->aux, cmd->ident, NG_L2CAP_ATT_CID,
131281198Stakawata				  NG_L2CAP_ATT_CID, 0, 0);
132281198Stakawata		cmd->aux->m_flags |= M_PROTO2;
133290038Stakawata	}else if(ip->idtype == NG_L2CAP_L2CA_IDTYPE_SMP){
134290038Stakawata		_ng_l2cap_con_rsp(cmd->aux, cmd->ident, NG_L2CAP_SMP_CID,
135290038Stakawata				  NG_L2CAP_SMP_CID, 0, 0);
136290038Stakawata		cmd->aux->m_flags |= M_PROTO2;
137281198Stakawata	}else{
138281198Stakawata		_ng_l2cap_con_req(cmd->aux, cmd->ident, ch->psm, ch->scid);
139281198Stakawata	}
140107120Sjulian	if (cmd->aux == NULL) {
141107120Sjulian		ng_l2cap_free_cmd(cmd);
142107120Sjulian		ng_l2cap_free_chan(ch);
143107120Sjulian		error = ENOBUFS;
144107120Sjulian		goto out;
145107120Sjulian	}
146107120Sjulian
147107120Sjulian	ch->state = NG_L2CAP_W4_L2CAP_CON_RSP;
148107120Sjulian
149107120Sjulian	/* Link command to the queue */
150107120Sjulian	ng_l2cap_link_cmd(ch->con, cmd);
151107120Sjulian	ng_l2cap_lp_deliver(ch->con);
152107120Sjulianout:
153107120Sjulian	return (error);
154107120Sjulian} /* ng_l2cap_l2ca_con_req */
155107120Sjulian
156107120Sjulian/*
157107120Sjulian * Send L2CA_Connect response to the upper layer protocol.
158107120Sjulian */
159107120Sjulian
160107120Sjulianint
161107120Sjulianng_l2cap_l2ca_con_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result,
162107120Sjulian		u_int16_t status)
163107120Sjulian{
164107120Sjulian	ng_l2cap_p		 l2cap = ch->con->l2cap;
165107120Sjulian	struct ng_mesg		*msg = NULL;
166107120Sjulian	ng_l2cap_l2ca_con_op	*op = NULL;
167107120Sjulian	int			 error = 0;
168107120Sjulian
169107120Sjulian	/* Check if upstream hook is connected and valid */
170107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
171107120Sjulian		NG_L2CAP_ERR(
172107120Sjulian"%s: %s - unable to send L2CA_Connect response message. " \
173107120Sjulian"Hook is not connected or valid, psm=%d\n",
174107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
175107120Sjulian
176107120Sjulian		return (ENOTCONN);
177107120Sjulian	}
178107120Sjulian
179107120Sjulian	/* Create and send L2CA_Connect response message */
180107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON,
181107120Sjulian		sizeof(*op), M_NOWAIT);
182107120Sjulian	if (msg == NULL)
183107120Sjulian		error = ENOMEM;
184107120Sjulian	else {
185107120Sjulian		msg->header.token = token;
186107120Sjulian		msg->header.flags |= NGF_RESP;
187107120Sjulian
188107120Sjulian		op = (ng_l2cap_l2ca_con_op *)(msg->data);
189107120Sjulian
190107120Sjulian		/*
191107120Sjulian		 * XXX Spec. says we should only populate LCID when result == 0
192107120Sjulian		 * What about PENDING? What the heck, for now always populate
193107120Sjulian		 * LCID :)
194107120Sjulian		 */
195281198Stakawata		if(ch->scid == NG_L2CAP_ATT_CID){
196281198Stakawata			op->idtype = NG_L2CAP_L2CA_IDTYPE_ATT;
197281198Stakawata			op->lcid = ch->con->con_handle;
198290038Stakawata		}else if(ch->scid == NG_L2CAP_SMP_CID){
199290038Stakawata			op->idtype = NG_L2CAP_L2CA_IDTYPE_SMP;
200290038Stakawata			op->lcid = ch->con->con_handle;
201281198Stakawata		}else{
202281198Stakawata			op->idtype = (ch->con->linktype == NG_HCI_LINK_ACL)?
203281198Stakawata				NG_L2CAP_L2CA_IDTYPE_BREDR :
204281198Stakawata				NG_L2CAP_L2CA_IDTYPE_LE;
205281198Stakawata			op->lcid = ch->scid;
206281198Stakawata		}
207290038Stakawata		op->encryption = ch->con->encryption;
208107120Sjulian		op->result = result;
209107120Sjulian		op->status = status;
210107120Sjulian
211128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
212107120Sjulian	}
213107120Sjulian
214107120Sjulian	return (error);
215107120Sjulian} /* ng_l2cap_l2ca_con_rsp */
216107120Sjulian
217107120Sjulian/*
218107120Sjulian * Process L2CA_ConnectRsp request from the upper layer protocol.
219107120Sjulian */
220107120Sjulian
221107120Sjulianint
222107120Sjulianng_l2cap_l2ca_con_rsp_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
223107120Sjulian{
224107120Sjulian	ng_l2cap_l2ca_con_rsp_ip	*ip = NULL;
225107120Sjulian	ng_l2cap_con_p			 con = NULL;
226107120Sjulian	ng_l2cap_chan_p			 ch = NULL;
227107120Sjulian	ng_l2cap_cmd_p			 cmd = NULL;
228107120Sjulian	u_int16_t			 dcid;
229107120Sjulian	int				 error = 0;
230107120Sjulian
231107120Sjulian	/* Check message */
232107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
233107120Sjulian		NG_L2CAP_ALERT(
234107120Sjulian"%s: %s - invalid L2CA_ConnectRsp request message size, size=%d\n",
235107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
236107120Sjulian			msg->header.arglen);
237107120Sjulian		error = EMSGSIZE;
238107120Sjulian		goto out;
239107120Sjulian	}
240107120Sjulian
241107120Sjulian	ip = (ng_l2cap_l2ca_con_rsp_ip *)(msg->data);
242107120Sjulian
243107120Sjulian	/* Check if we have this channel */
244290038Stakawata	if((ip->lcid != NG_L2CAP_ATT_CID)&&
245290038Stakawata	   (ip->lcid != NG_L2CAP_SMP_CID)){
246281198Stakawata		ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid
247281198Stakawata					   ,(ip->linktype == NG_HCI_LINK_ACL)?
248281198Stakawata					   NG_L2CAP_L2CA_IDTYPE_BREDR:
249281198Stakawata					   NG_L2CAP_L2CA_IDTYPE_LE);
250281198Stakawata	}else{
251281198Stakawata		// For now not support on ATT device.
252281198Stakawata		ch = NULL;
253281198Stakawata	}
254107120Sjulian	if (ch == NULL) {
255107120Sjulian		NG_L2CAP_ALERT(
256107120Sjulian"%s: %s - unexpected L2CA_ConnectRsp request message. " \
257107120Sjulian"Channel does not exist, lcid=%d\n",
258107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
259107120Sjulian		error = ENOENT;
260107120Sjulian		goto out;
261107120Sjulian	}
262107120Sjulian
263107120Sjulian	/* Check channel state */
264107120Sjulian	if (ch->state != NG_L2CAP_W4_L2CA_CON_RSP) {
265107120Sjulian		NG_L2CAP_ERR(
266107120Sjulian"%s: %s - unexpected L2CA_ConnectRsp request message. " \
267107120Sjulian"Invalid channel state, state=%d, lcid=%d\n",
268107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->state,
269107120Sjulian			ip->lcid);
270107120Sjulian		error = EINVAL;
271107120Sjulian		goto out;
272107120Sjulian	}
273107120Sjulian
274107120Sjulian	dcid = ch->dcid;
275107120Sjulian	con = ch->con;
276107120Sjulian
277107120Sjulian	/*
278107120Sjulian	 * Now we are pretty much sure it is our response. So create and send
279107120Sjulian	 * L2CAP_ConnectRsp message to our peer.
280107120Sjulian	 */
281107120Sjulian
282107120Sjulian	if (ch->ident != ip->ident)
283107120Sjulian		NG_L2CAP_WARN(
284107120Sjulian"%s: %s - channel ident and response ident do not match, scid=%d, ident=%d. " \
285107120Sjulian"Will use response ident=%d\n",
286107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->scid,
287107120Sjulian			ch->ident, ip->ident);
288107120Sjulian
289107120Sjulian	/* Check result */
290107120Sjulian	switch (ip->result) {
291107120Sjulian	case NG_L2CAP_SUCCESS:
292290038Stakawata		ch->state = ((ch->scid == NG_L2CAP_ATT_CID)||
293290038Stakawata			     (ch->scid == NG_L2CAP_SMP_CID))?
294281198Stakawata			NG_L2CAP_OPEN : NG_L2CAP_CONFIG;
295107120Sjulian		ch->cfg_state = 0;
296107120Sjulian		break;
297107120Sjulian
298107120Sjulian	case NG_L2CAP_PENDING:
299107120Sjulian		break;
300107120Sjulian
301107120Sjulian	default:
302107120Sjulian		ng_l2cap_free_chan(ch);
303107120Sjulian		ch = NULL;
304107120Sjulian		break;
305107120Sjulian	}
306107120Sjulian
307107120Sjulian	/* Create L2CAP command */
308107120Sjulian	cmd = ng_l2cap_new_cmd(con, ch, ip->ident, NG_L2CAP_CON_RSP,
309107120Sjulian			msg->header.token);
310107120Sjulian	if (cmd == NULL) {
311107120Sjulian		if (ch != NULL)
312107120Sjulian			ng_l2cap_free_chan(ch);
313107120Sjulian
314107120Sjulian		error = ENOMEM;
315107120Sjulian		goto out;
316107120Sjulian	}
317107120Sjulian
318107120Sjulian	_ng_l2cap_con_rsp(cmd->aux, cmd->ident, ip->lcid, dcid,
319107120Sjulian		ip->result, ip->status);
320107120Sjulian	if (cmd->aux == NULL) {
321107120Sjulian		if (ch != NULL)
322107120Sjulian			ng_l2cap_free_chan(ch);
323107120Sjulian
324107120Sjulian		ng_l2cap_free_cmd(cmd);
325107120Sjulian		error = ENOBUFS;
326107120Sjulian		goto out;
327107120Sjulian	}
328107120Sjulian
329107120Sjulian	/* Link command to the queue */
330107120Sjulian	ng_l2cap_link_cmd(con, cmd);
331107120Sjulian	ng_l2cap_lp_deliver(con);
332107120Sjulianout:
333107120Sjulian	return (error);
334107120Sjulian} /* ng_l2cap_l2ca_con_rsp_req */
335107120Sjulian
336290038Stakawataint ng_l2cap_l2ca_encryption_change(ng_l2cap_chan_p ch, uint16_t result)
337290038Stakawata{
338290038Stakawata	ng_l2cap_p			 l2cap = ch->con->l2cap;
339290038Stakawata	struct ng_mesg			*msg = NULL;
340290038Stakawata	ng_l2cap_l2ca_enc_chg_op	*op = NULL;
341290038Stakawata	int				 error = 0;
342290038Stakawata
343290038Stakawata	/* Check if upstream hook is connected and valid */
344290038Stakawata	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
345290038Stakawata		NG_L2CAP_ERR(
346290038Stakawata"%s: %s - unable to send L2CA_ConnectRsp response message. " \
347290038Stakawata"Hook is not connected or valid, psm=%d\n",
348290038Stakawata			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
349290038Stakawata
350290038Stakawata		return (ENOTCONN);
351290038Stakawata	}
352290038Stakawata
353290038Stakawata	/* Create and send L2CA_ConnectRsp response message */
354290038Stakawata	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_ENC_CHANGE,
355290038Stakawata		sizeof(*op), M_NOWAIT);
356290038Stakawata	if (msg == NULL)
357290038Stakawata		error = ENOMEM;
358290038Stakawata	else {
359290038Stakawata		msg->header.token = 0;
360290038Stakawata		msg->header.flags |= NGF_RESP;
361290038Stakawata
362290038Stakawata		op = (ng_l2cap_l2ca_enc_chg_op *)(msg->data);
363290038Stakawata		op->result = result;
364290038Stakawata		if(ch->scid ==NG_L2CAP_ATT_CID||
365290038Stakawata		   ch->scid ==NG_L2CAP_SMP_CID){
366290038Stakawata			op->lcid = ch->con->con_handle;
367290038Stakawata			op->idtype = (ch->scid==NG_L2CAP_ATT_CID)?
368290038Stakawata				NG_L2CAP_L2CA_IDTYPE_ATT:
369290038Stakawata				NG_L2CAP_L2CA_IDTYPE_SMP;
370290038Stakawata		}else{
371290038Stakawata			op->idtype =(ch->con->linktype ==NG_HCI_LINK_ACL)?
372290038Stakawata				NG_L2CAP_L2CA_IDTYPE_BREDR:
373290038Stakawata				NG_L2CAP_L2CA_IDTYPE_LE;
374290038Stakawata		}
375290038Stakawata
376290038Stakawata
377290038Stakawata		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
378290038Stakawata	}
379290038Stakawata
380290038Stakawata	return (error);
381290038Stakawata
382290038Stakawata}
383107120Sjulian/*
384107120Sjulian * Send L2CAP_ConnectRsp response to the upper layer
385107120Sjulian */
386107120Sjulian
387107120Sjulianint
388107120Sjulianng_l2cap_l2ca_con_rsp_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
389107120Sjulian{
390107120Sjulian	ng_l2cap_p			 l2cap = ch->con->l2cap;
391107120Sjulian	struct ng_mesg			*msg = NULL;
392107120Sjulian	ng_l2cap_l2ca_con_rsp_op	*op = NULL;
393107120Sjulian	int				 error = 0;
394107120Sjulian
395107120Sjulian	/* Check if upstream hook is connected and valid */
396107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
397107120Sjulian		NG_L2CAP_ERR(
398107120Sjulian"%s: %s - unable to send L2CA_ConnectRsp response message. " \
399107120Sjulian"Hook is not connected or valid, psm=%d\n",
400107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
401107120Sjulian
402107120Sjulian		return (ENOTCONN);
403107120Sjulian	}
404107120Sjulian
405107120Sjulian	/* Create and send L2CA_ConnectRsp response message */
406107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_RSP,
407107120Sjulian		sizeof(*op), M_NOWAIT);
408107120Sjulian	if (msg == NULL)
409107120Sjulian		error = ENOMEM;
410107120Sjulian	else {
411107120Sjulian		msg->header.token = token;
412107120Sjulian		msg->header.flags |= NGF_RESP;
413107120Sjulian
414107120Sjulian		op = (ng_l2cap_l2ca_con_rsp_op *)(msg->data);
415107120Sjulian		op->result = result;
416107120Sjulian
417128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
418107120Sjulian	}
419107120Sjulian
420107120Sjulian	return (error);
421107120Sjulian} /* ng_l2cap_l2ca_con_rsp_rsp */
422107120Sjulian
423107120Sjulian/*
424107120Sjulian * Send L2CA_ConnectInd message to the upper layer protocol.
425107120Sjulian */
426107120Sjulian
427107120Sjulianint
428107120Sjulianng_l2cap_l2ca_con_ind(ng_l2cap_chan_p ch)
429107120Sjulian{
430107120Sjulian	ng_l2cap_p			 l2cap = ch->con->l2cap;
431107120Sjulian	struct ng_mesg			*msg = NULL;
432107120Sjulian	ng_l2cap_l2ca_con_ind_ip	*ip = NULL;
433107120Sjulian	int				 error = 0;
434107120Sjulian
435107120Sjulian	/* Check if upstream hook is connected and valid */
436107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
437107120Sjulian		NG_L2CAP_ERR(
438107120Sjulian"%s: %s - unable to send L2CA_ConnectInd message. " \
439107120Sjulian"Hook is not connected or valid, psm=%d\n",
440107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
441107120Sjulian
442107120Sjulian		return (ENOTCONN);
443107120Sjulian	}
444107120Sjulian
445107120Sjulian	/* Create and send L2CA_ConnectInd message */
446107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_IND,
447107120Sjulian		sizeof(*ip), M_NOWAIT);
448107120Sjulian	if (msg == NULL)
449107120Sjulian		error = ENOMEM;
450107120Sjulian	else {
451107120Sjulian		ip = (ng_l2cap_l2ca_con_ind_ip *)(msg->data);
452107120Sjulian
453107120Sjulian		bcopy(&ch->con->remote, &ip->bdaddr, sizeof(ip->bdaddr));
454107120Sjulian		ip->lcid = ch->scid;
455107120Sjulian		ip->psm = ch->psm;
456107120Sjulian		ip->ident = ch->ident;
457285244Stakawata		ip->linktype = ch->con->linktype;
458290038Stakawata
459128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
460107120Sjulian	}
461107120Sjulian
462107120Sjulian	return (error);
463107120Sjulian} /* ng_l2cap_l2ca_con_ind */
464107120Sjulian
465107120Sjulian/*
466107120Sjulian * Process L2CA_Config request from the upper layer protocol
467107120Sjulian */
468107120Sjulian
469107120Sjulianint
470107120Sjulianng_l2cap_l2ca_cfg_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
471107120Sjulian{
472107120Sjulian	ng_l2cap_l2ca_cfg_ip	*ip = NULL;
473107120Sjulian	ng_l2cap_chan_p		 ch = NULL;
474107120Sjulian	ng_l2cap_cmd_p		 cmd = NULL;
475107120Sjulian	struct mbuf		*opt = NULL;
476107120Sjulian        u_int16_t		*mtu = NULL, *flush_timo = NULL;
477107120Sjulian        ng_l2cap_flow_p		 flow = NULL;
478107120Sjulian	int			 error = 0;
479107120Sjulian
480107120Sjulian	/* Check message */
481107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
482107120Sjulian		NG_L2CAP_ALERT(
483107120Sjulian"%s: %s - Invalid L2CA_Config request message size, size=%d\n",
484107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
485107120Sjulian			msg->header.arglen);
486107120Sjulian		error = EMSGSIZE;
487107120Sjulian		goto out;
488107120Sjulian	}
489107120Sjulian
490107120Sjulian	ip = (ng_l2cap_l2ca_cfg_ip *)(msg->data);
491107120Sjulian
492107120Sjulian	/* Check if we have this channel */
493281198Stakawata	ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid, NG_L2CAP_L2CA_IDTYPE_BREDR);
494107120Sjulian	if (ch == NULL) {
495107120Sjulian		NG_L2CAP_ERR(
496107120Sjulian"%s: %s - unexpected L2CA_Config request message. " \
497107120Sjulian"Channel does not exist, lcid=%d\n",
498107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
499107120Sjulian		error = ENOENT;
500107120Sjulian		goto out;
501107120Sjulian	}
502107120Sjulian
503107120Sjulian	/* Check channel state */
504107120Sjulian	if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG) {
505107120Sjulian		NG_L2CAP_ERR(
506107120Sjulian"%s: %s - unexpected L2CA_Config request message. " \
507107120Sjulian"Invalid channel state, state=%d, lcid=%d\n",
508107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->state,
509107120Sjulian			ch->scid);
510107120Sjulian		error = EINVAL;
511107120Sjulian		goto out;
512107120Sjulian	}
513107120Sjulian
514107120Sjulian	/* Set requested channel configuration options */
515107120Sjulian	ch->imtu = ip->imtu;
516107120Sjulian	bcopy(&ip->oflow, &ch->oflow, sizeof(ch->oflow));
517107120Sjulian	ch->flush_timo = ip->flush_timo;
518107120Sjulian	ch->link_timo = ip->link_timo;
519107120Sjulian
520107120Sjulian	/* Compare channel settings with defaults */
521107120Sjulian	if (ch->imtu != NG_L2CAP_MTU_DEFAULT)
522107120Sjulian		mtu = &ch->imtu;
523107120Sjulian	if (ch->flush_timo != NG_L2CAP_FLUSH_TIMO_DEFAULT)
524107120Sjulian		flush_timo = &ch->flush_timo;
525107120Sjulian	if (bcmp(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow)) != 0)
526107120Sjulian		flow = &ch->oflow;
527107120Sjulian
528107120Sjulian	/* Create configuration options */
529107120Sjulian	_ng_l2cap_build_cfg_options(opt, mtu, flush_timo, flow);
530107120Sjulian	if (opt == NULL) {
531107120Sjulian                error = ENOBUFS;
532107120Sjulian		goto out;
533107120Sjulian	}
534107120Sjulian
535107120Sjulian	/* Create L2CAP command descriptor */
536107120Sjulian	cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(ch->con),
537107120Sjulian			NG_L2CAP_CFG_REQ, msg->header.token);
538107120Sjulian	if (cmd == NULL) {
539107120Sjulian		NG_FREE_M(opt);
540107120Sjulian		error = ENOMEM;
541107120Sjulian		goto out;
542107120Sjulian	}
543107120Sjulian
544107120Sjulian	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
545107120Sjulian		ng_l2cap_free_cmd(cmd);
546107120Sjulian		NG_FREE_M(opt);
547107120Sjulian		error = EIO;
548107120Sjulian		goto out;
549107120Sjulian	}
550107120Sjulian
551107120Sjulian	/* Create L2CAP command packet */
552107120Sjulian	_ng_l2cap_cfg_req(cmd->aux, cmd->ident, ch->dcid, 0, opt);
553107120Sjulian	if (cmd->aux == NULL) {
554107120Sjulian		ng_l2cap_free_cmd(cmd);
555107120Sjulian		error =  ENOBUFS;
556107120Sjulian		goto out;
557107120Sjulian	}
558107120Sjulian
559107120Sjulian	/* Adjust channel state for re-configuration */
560107120Sjulian	if (ch->state == NG_L2CAP_OPEN) {
561290038Stakawata		ch->state = ((ch->scid == NG_L2CAP_ATT_CID)||
562290038Stakawata			     (ch->scid == NG_L2CAP_SMP_CID))?
563281198Stakawata			NG_L2CAP_OPEN : NG_L2CAP_CONFIG;
564107120Sjulian		ch->cfg_state = 0;
565107120Sjulian	}
566107120Sjulian
567107120Sjulian        /* Link command to the queue */
568107120Sjulian	ng_l2cap_link_cmd(ch->con, cmd);
569107120Sjulian	ng_l2cap_lp_deliver(ch->con);
570107120Sjulianout:
571107120Sjulian	return (error);
572107120Sjulian} /* ng_l2cap_l2ca_cfg_req */
573107120Sjulian
574107120Sjulian/*
575107120Sjulian * Send L2CA_Config response to the upper layer protocol
576107120Sjulian */
577107120Sjulian
578107120Sjulianint
579107120Sjulianng_l2cap_l2ca_cfg_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
580107120Sjulian{
581107120Sjulian	ng_l2cap_p		 l2cap = ch->con->l2cap;
582107120Sjulian	struct ng_mesg		*msg = NULL;
583107120Sjulian	ng_l2cap_l2ca_cfg_op	*op = NULL;
584107120Sjulian	int			 error = 0;
585107120Sjulian
586107120Sjulian	/* Check if upstream hook is connected and valid */
587107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
588107120Sjulian		NG_L2CAP_ERR(
589107120Sjulian"%s: %s - unable to send L2CA_Config response message. " \
590107120Sjulian"Hook is not connected or valid, psm=%d\n",
591107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
592107120Sjulian
593107120Sjulian		return (ENOTCONN);
594107120Sjulian	}
595107120Sjulian
596107120Sjulian	/* Create and send L2CA_Config response message */
597107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG,
598107120Sjulian		sizeof(*op), M_NOWAIT);
599107120Sjulian	if (msg == NULL)
600107120Sjulian		error = ENOMEM;
601107120Sjulian	else {
602107120Sjulian		msg->header.token = token;
603107120Sjulian		msg->header.flags |= NGF_RESP;
604107120Sjulian
605107120Sjulian		op = (ng_l2cap_l2ca_cfg_op *)(msg->data);
606107120Sjulian		op->result = result;
607107120Sjulian		op->imtu = ch->imtu;
608107120Sjulian		bcopy(&ch->oflow, &op->oflow, sizeof(op->oflow));
609107120Sjulian		op->flush_timo = ch->flush_timo;
610107120Sjulian
611128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
612107120Sjulian
613107120Sjulian		if (error == 0 && result == NG_L2CAP_SUCCESS) {
614107120Sjulian			ch->cfg_state |= NG_L2CAP_CFG_IN;
615107120Sjulian
616107120Sjulian			if (ch->cfg_state == NG_L2CAP_CFG_BOTH)
617107120Sjulian				ch->state = NG_L2CAP_OPEN;
618107120Sjulian		}
619107120Sjulian	}
620107120Sjulian
621107120Sjulian	return (error);
622107120Sjulian} /* ng_l2cap_l2ca_cfg_rsp */
623107120Sjulian
624107120Sjulian/*
625107120Sjulian * Process L2CA_ConfigRsp request from the upper layer protocol
626107120Sjulian *
627107120Sjulian * XXX XXX XXX
628107120Sjulian *
629107120Sjulian * NOTE: The Bluetooth specification says that Configuration_Response
630107120Sjulian * (L2CA_ConfigRsp) should be used to issue response to configuration request
631107120Sjulian * indication. The minor problem here is L2CAP command ident. We should use
632107120Sjulian * ident from original L2CAP request to make sure our peer can match request
633107120Sjulian * and response. For some reason Bluetooth specification does not include
634107120Sjulian * ident field into L2CA_ConfigInd and L2CA_ConfigRsp messages. This seems
635107120Sjulian * strange to me, because L2CA_ConnectInd and L2CA_ConnectRsp do have ident
636107120Sjulian * field. So we should store last known L2CAP request command ident in channel.
637107120Sjulian * Also it seems that upper layer can not reject configuration request, as
638107120Sjulian * Configuration_Response message does not have status/reason field.
639107120Sjulian */
640107120Sjulian
641107120Sjulianint
642107120Sjulianng_l2cap_l2ca_cfg_rsp_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
643107120Sjulian{
644107120Sjulian	ng_l2cap_l2ca_cfg_rsp_ip	*ip = NULL;
645107120Sjulian	ng_l2cap_chan_p			 ch = NULL;
646107120Sjulian	ng_l2cap_cmd_p			 cmd = NULL;
647107120Sjulian	struct mbuf			*opt = NULL;
648107120Sjulian	u_int16_t			*mtu = NULL;
649107120Sjulian	ng_l2cap_flow_p			 flow = NULL;
650107120Sjulian	int				 error = 0;
651107120Sjulian
652107120Sjulian	/* Check message */
653107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
654107120Sjulian		NG_L2CAP_ALERT(
655107120Sjulian"%s: %s - invalid L2CA_ConfigRsp request message size, size=%d\n",
656107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
657107120Sjulian			msg->header.arglen);
658107120Sjulian		error = EMSGSIZE;
659107120Sjulian		goto out;
660107120Sjulian	}
661107120Sjulian
662107120Sjulian	ip = (ng_l2cap_l2ca_cfg_rsp_ip *)(msg->data);
663107120Sjulian
664107120Sjulian	/* Check if we have this channel */
665281198Stakawata	ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid,
666281198Stakawata				   NG_L2CAP_L2CA_IDTYPE_BREDR);
667107120Sjulian	if (ch == NULL) {
668107120Sjulian		NG_L2CAP_ERR(
669107120Sjulian"%s: %s - unexpected L2CA_ConfigRsp request message. " \
670107120Sjulian"Channel does not exist, lcid=%d\n",
671107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
672107120Sjulian		error = ENOENT;
673107120Sjulian		goto out;
674107120Sjulian	}
675107120Sjulian
676107120Sjulian	/* Check channel state */
677107120Sjulian	if (ch->state != NG_L2CAP_CONFIG) {
678107120Sjulian		NG_L2CAP_ERR(
679107120Sjulian"%s: %s - unexpected L2CA_ConfigRsp request message. " \
680107120Sjulian"Invalid channel state, state=%d, lcid=%d\n",
681107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->state,
682107120Sjulian			ch->scid);
683107120Sjulian		error = EINVAL;
684107120Sjulian		goto out;
685107120Sjulian	}
686107120Sjulian
687107120Sjulian	/* Set channel settings */
688107120Sjulian	if (ip->omtu != ch->omtu) {
689107120Sjulian		ch->omtu = ip->omtu;
690107120Sjulian		mtu = &ch->omtu;
691107120Sjulian	}
692107120Sjulian
693107120Sjulian	if (bcmp(&ip->iflow, &ch->iflow, sizeof(ch->iflow)) != 0) {
694107120Sjulian		bcopy(&ip->iflow, &ch->iflow, sizeof(ch->iflow));
695107120Sjulian		flow = &ch->iflow;
696107120Sjulian	}
697107120Sjulian
698107120Sjulian	if (mtu != NULL || flow != NULL) {
699107120Sjulian		_ng_l2cap_build_cfg_options(opt, mtu, NULL, flow);
700107120Sjulian		if (opt == NULL) {
701107120Sjulian			error = ENOBUFS;
702107120Sjulian			goto out;
703107120Sjulian		}
704107120Sjulian	}
705107120Sjulian
706107120Sjulian	/* Create L2CAP command */
707107120Sjulian	cmd = ng_l2cap_new_cmd(ch->con, ch, ch->ident, NG_L2CAP_CFG_RSP,
708107120Sjulian			msg->header.token);
709107120Sjulian	if (cmd == NULL) {
710107120Sjulian		NG_FREE_M(opt);
711107120Sjulian		error = ENOMEM;
712107120Sjulian		goto out;
713107120Sjulian	}
714107120Sjulian
715107120Sjulian	_ng_l2cap_cfg_rsp(cmd->aux,cmd->ident,ch->dcid,0,NG_L2CAP_SUCCESS,opt);
716107120Sjulian	if (cmd->aux == NULL) {
717107120Sjulian		ng_l2cap_free_cmd(cmd);
718107120Sjulian		error = ENOBUFS;
719107120Sjulian		goto out;
720107120Sjulian	}
721107120Sjulian
722107120Sjulian	/* XXX FIXME - not here ??? */
723107120Sjulian	ch->cfg_state |= NG_L2CAP_CFG_OUT;
724107120Sjulian	if (ch->cfg_state == NG_L2CAP_CFG_BOTH)
725107120Sjulian		ch->state = NG_L2CAP_OPEN;
726107120Sjulian
727107120Sjulian	/* Link command to the queue */
728107120Sjulian	ng_l2cap_link_cmd(ch->con, cmd);
729107120Sjulian	ng_l2cap_lp_deliver(ch->con);
730107120Sjulianout:
731107120Sjulian	return (error);
732107120Sjulian} /* ng_l2cap_l2ca_cfg_rsp_req */
733107120Sjulian
734107120Sjulian/*
735107120Sjulian * Send L2CA_ConfigRsp response to the upper layer protocol
736107120Sjulian */
737107120Sjulian
738107120Sjulianint
739107120Sjulianng_l2cap_l2ca_cfg_rsp_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
740107120Sjulian{
741107120Sjulian	ng_l2cap_p			 l2cap = ch->con->l2cap;
742107120Sjulian	struct ng_mesg			*msg = NULL;
743107120Sjulian	ng_l2cap_l2ca_cfg_rsp_op	*op = NULL;
744107120Sjulian	int				 error = 0;
745107120Sjulian
746107120Sjulian	/* Check if upstream hook is connected and valid */
747107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
748107120Sjulian		NG_L2CAP_ERR(
749107120Sjulian"%s: %s - unable to send L2CA_ConfigRsp response message. " \
750107120Sjulian"Hook is not connected or valid, psm=%d\n",
751107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
752107120Sjulian
753107120Sjulian		return (ENOTCONN);
754107120Sjulian	}
755107120Sjulian
756107120Sjulian	/* Create and send L2CA_ConfigRsp response message */
757107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_RSP,
758107120Sjulian		sizeof(*op), M_NOWAIT);
759107120Sjulian	if (msg == NULL)
760107120Sjulian		error = ENOMEM;
761107120Sjulian	else {
762107120Sjulian		msg->header.token = token;
763107120Sjulian		msg->header.flags |= NGF_RESP;
764107120Sjulian
765107120Sjulian		op = (ng_l2cap_l2ca_cfg_rsp_op *)(msg->data);
766107120Sjulian		op->result = result;
767107120Sjulian
768128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
769107120Sjulian	}
770107120Sjulian
771107120Sjulian	return (error);
772107120Sjulian} /* ng_l2cap_l2ca_cfg_rsp_rsp */
773107120Sjulian
774107120Sjulian/*
775107120Sjulian * Send L2CA_ConfigInd message to the upper layer protocol
776107120Sjulian *
777107120Sjulian * XXX XXX XXX
778107120Sjulian *
779107120Sjulian * NOTE: The Bluetooth specification says that Configuration_Response
780107120Sjulian * (L2CA_ConfigRsp) should be used to issue response to configuration request
781107120Sjulian * indication. The minor problem here is L2CAP command ident. We should use
782107120Sjulian * ident from original L2CAP request to make sure our peer can match request
783107120Sjulian * and response. For some reason Bluetooth specification does not include
784107120Sjulian * ident field into L2CA_ConfigInd and L2CA_ConfigRsp messages. This seems
785107120Sjulian * strange to me, because L2CA_ConnectInd and L2CA_ConnectRsp do have ident
786107120Sjulian * field. So we should store last known L2CAP request command ident in channel.
787107120Sjulian * Also it seems that upper layer can not reject configuration request, as
788107120Sjulian * Configuration_Response message does not have status/reason field.
789107120Sjulian */
790107120Sjulian
791107120Sjulianint
792107120Sjulianng_l2cap_l2ca_cfg_ind(ng_l2cap_chan_p ch)
793107120Sjulian{
794107120Sjulian	ng_l2cap_p			 l2cap = ch->con->l2cap;
795107120Sjulian	struct ng_mesg			*msg = NULL;
796107120Sjulian	ng_l2cap_l2ca_cfg_ind_ip	*ip = NULL;
797107120Sjulian	int				 error = 0;
798107120Sjulian
799107120Sjulian	/* Check if upstream hook is connected and valid */
800107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
801107120Sjulian		NG_L2CAP_ERR(
802107120Sjulian"%s: %s - Unable to send L2CA_ConfigInd message. " \
803107120Sjulian"Hook is not connected or valid, psm=%d\n",
804107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
805107120Sjulian
806107120Sjulian		return (ENOTCONN);
807107120Sjulian	}
808107120Sjulian
809107120Sjulian	/* Create and send L2CA_ConnectInd message */
810107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_IND,
811107120Sjulian			sizeof(*ip), M_NOWAIT);
812107120Sjulian	if (msg == NULL)
813107120Sjulian		error = ENOMEM;
814107120Sjulian	else {
815107120Sjulian		ip = (ng_l2cap_l2ca_cfg_ind_ip *)(msg->data);
816107120Sjulian		ip->lcid = ch->scid;
817107120Sjulian		ip->omtu = ch->omtu;
818107120Sjulian		bcopy(&ch->iflow, &ip->iflow, sizeof(ip->iflow));
819107120Sjulian		ip->flush_timo = ch->flush_timo;
820107120Sjulian
821128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
822107120Sjulian	}
823107120Sjulian
824107120Sjulian	return (error);
825107120Sjulian} /* ng_l2cap_l2ca_cfg_ind */
826107120Sjulian
827107120Sjulian/*
828107120Sjulian * Process L2CA_Write event
829107120Sjulian */
830107120Sjulian
831107120Sjulianint
832107120Sjulianng_l2cap_l2ca_write_req(ng_l2cap_p l2cap, struct mbuf *m)
833107120Sjulian{
834107120Sjulian	ng_l2cap_l2ca_hdr_t	*l2ca_hdr = NULL;
835107120Sjulian	ng_l2cap_chan_p		 ch = NULL;
836107120Sjulian	ng_l2cap_cmd_p		 cmd = NULL;
837107120Sjulian	int			 error = 0;
838107120Sjulian	u_int32_t		 token = 0;
839107120Sjulian
840107120Sjulian	/* Make sure we can access L2CA data packet header */
841107120Sjulian	if (m->m_pkthdr.len < sizeof(*l2ca_hdr)) {
842107120Sjulian		NG_L2CAP_ERR(
843107120Sjulian"%s: %s - L2CA Data packet too small, len=%d\n",
844107120Sjulian			__func__,NG_NODE_NAME(l2cap->node),m->m_pkthdr.len);
845107120Sjulian		error = EMSGSIZE;
846107120Sjulian		goto drop;
847107120Sjulian	}
848107120Sjulian
849107120Sjulian	/* Get L2CA data packet header */
850107120Sjulian	NG_L2CAP_M_PULLUP(m, sizeof(*l2ca_hdr));
851107120Sjulian	if (m == NULL)
852107120Sjulian		return (ENOBUFS);
853107120Sjulian
854107120Sjulian	l2ca_hdr = mtod(m, ng_l2cap_l2ca_hdr_t *);
855107120Sjulian	token = l2ca_hdr->token;
856107120Sjulian	m_adj(m, sizeof(*l2ca_hdr));
857107120Sjulian
858107120Sjulian	/* Verify payload size */
859107120Sjulian	if (l2ca_hdr->length != m->m_pkthdr.len) {
860107120Sjulian		NG_L2CAP_ERR(
861107120Sjulian"%s: %s - invalid L2CA Data packet. " \
862107120Sjulian"Payload length does not match, length=%d, len=%d\n",
863107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->length,
864107120Sjulian			m->m_pkthdr.len);
865107120Sjulian		error = EMSGSIZE;
866107120Sjulian		goto drop;
867107120Sjulian	}
868107120Sjulian
869107120Sjulian	/* Check channel ID */
870281198Stakawata	if (l2ca_hdr->idtype == NG_L2CAP_L2CA_IDTYPE_ATT){
871281198Stakawata		ch = ng_l2cap_chan_by_conhandle(l2cap, NG_L2CAP_ATT_CID,
872281198Stakawata						l2ca_hdr->lcid);
873290038Stakawata	} else if (l2ca_hdr->idtype == NG_L2CAP_L2CA_IDTYPE_SMP){
874290038Stakawata		ch = ng_l2cap_chan_by_conhandle(l2cap, NG_L2CAP_SMP_CID,
875290038Stakawata						l2ca_hdr->lcid);
876290038Stakawata	}else{
877281198Stakawata		if (l2ca_hdr->lcid < NG_L2CAP_FIRST_CID) {
878281198Stakawata			NG_L2CAP_ERR(
879281198Stakawata				"%s: %s - invalid L2CA Data packet. Inavlid channel ID, cid=%d\n",
880281198Stakawata				__func__, NG_NODE_NAME(l2cap->node),
881281198Stakawata				l2ca_hdr->lcid);
882281198Stakawata			error = EINVAL;
883281198Stakawata			goto drop;
884281198Stakawata		}
885281198Stakawata
886281198Stakawata		/* Verify that we have the channel and make sure it is open */
887281198Stakawata		ch = ng_l2cap_chan_by_scid(l2cap, l2ca_hdr->lcid,
888281198Stakawata					   l2ca_hdr->idtype);
889107120Sjulian	}
890281198Stakawata
891107120Sjulian	if (ch == NULL) {
892107120Sjulian		NG_L2CAP_ERR(
893107120Sjulian"%s: %s - invalid L2CA Data packet. Channel does not exist, cid=%d\n",
894107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->lcid);
895107120Sjulian		error = ENOENT;
896107120Sjulian		goto drop;
897107120Sjulian	}
898107120Sjulian
899107120Sjulian	if (ch->state != NG_L2CAP_OPEN) {
900107120Sjulian		NG_L2CAP_ERR(
901107120Sjulian"%s: %s - invalid L2CA Data packet. Invalid channel state, scid=%d, state=%d\n",
902107120Sjulian			 __func__, NG_NODE_NAME(l2cap->node), ch->scid,
903107120Sjulian			ch->state);
904107120Sjulian		error = EHOSTDOWN;
905107120Sjulian		goto drop; /* XXX not always - re-configure */
906107120Sjulian	}
907107120Sjulian
908107120Sjulian	/* Create L2CAP command descriptor */
909107120Sjulian	cmd = ng_l2cap_new_cmd(ch->con, ch, 0, NGM_L2CAP_L2CA_WRITE, token);
910107120Sjulian	if (cmd == NULL) {
911107120Sjulian		error = ENOMEM;
912107120Sjulian		goto drop;
913107120Sjulian	}
914107120Sjulian
915107120Sjulian	/* Attach data packet and link command to the queue */
916107120Sjulian	cmd->aux = m;
917107120Sjulian	ng_l2cap_link_cmd(ch->con, cmd);
918107120Sjulian	ng_l2cap_lp_deliver(ch->con);
919107120Sjulian
920107120Sjulian	return (error);
921107120Sjuliandrop:
922107120Sjulian	NG_FREE_M(m);
923107120Sjulian
924107120Sjulian	return (error);
925107120Sjulian} /* ng_l2cap_l2ca_write_req */
926107120Sjulian
927107120Sjulian/*
928107120Sjulian * Send L2CA_Write response
929107120Sjulian */
930107120Sjulian
931107120Sjulianint
932107120Sjulianng_l2cap_l2ca_write_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result,
933107120Sjulian		u_int16_t length)
934107120Sjulian{
935107120Sjulian	ng_l2cap_p		 l2cap = ch->con->l2cap;
936107120Sjulian	struct ng_mesg		*msg = NULL;
937107120Sjulian	ng_l2cap_l2ca_write_op	*op = NULL;
938107120Sjulian	int			 error = 0;
939107120Sjulian
940107120Sjulian	/* Check if upstream hook is connected and valid */
941107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
942107120Sjulian		NG_L2CAP_ERR(
943107120Sjulian"%s: %s - unable to send L2CA_WriteRsp message. " \
944107120Sjulian"Hook is not connected or valid, psm=%d\n",
945107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
946107120Sjulian
947107120Sjulian		return (ENOTCONN);
948107120Sjulian	}
949107120Sjulian
950107120Sjulian	/* Create and send L2CA_WriteRsp message */
951107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_WRITE,
952107120Sjulian			sizeof(*op), M_NOWAIT);
953107120Sjulian	if (msg == NULL)
954107120Sjulian		error = ENOMEM;
955107120Sjulian	else {
956107120Sjulian		msg->header.token = token;
957107120Sjulian		msg->header.flags |= NGF_RESP;
958107120Sjulian
959107120Sjulian		op = (ng_l2cap_l2ca_write_op *)(msg->data);
960107120Sjulian		op->result = result;
961107120Sjulian		op->length = length;
962281198Stakawata		if(ch->scid == NG_L2CAP_ATT_CID){
963281198Stakawata			op->idtype = NG_L2CAP_L2CA_IDTYPE_ATT;
964281198Stakawata			op->lcid = ch->con->con_handle;
965290038Stakawata		}else if(ch->scid == NG_L2CAP_SMP_CID){
966290038Stakawata			op->idtype = NG_L2CAP_L2CA_IDTYPE_SMP;
967290038Stakawata			op->lcid = ch->con->con_handle;
968281198Stakawata		}else{
969281198Stakawata			op->idtype = (ch->con->linktype == NG_HCI_LINK_ACL)?
970281198Stakawata				NG_L2CAP_L2CA_IDTYPE_BREDR :
971281198Stakawata				NG_L2CAP_L2CA_IDTYPE_LE;
972281198Stakawata			op->lcid = ch->scid;
973281198Stakawata
974281198Stakawata		}
975128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
976107120Sjulian	}
977107120Sjulian
978107120Sjulian	return (error);
979107120Sjulian} /* ng_l2cap_l2ca_write_rsp */
980107120Sjulian
981107120Sjulian/*
982107120Sjulian * Receive packet from the lower layer protocol and send it to the upper
983107120Sjulian * layer protocol (L2CAP_Read)
984107120Sjulian */
985107120Sjulian
986107120Sjulianint
987107120Sjulianng_l2cap_l2ca_receive(ng_l2cap_con_p con)
988107120Sjulian{
989107120Sjulian	ng_l2cap_p	 l2cap = con->l2cap;
990107120Sjulian	ng_l2cap_hdr_t	*hdr = NULL;
991107120Sjulian	ng_l2cap_chan_p  ch = NULL;
992107120Sjulian	int		 error = 0;
993281198Stakawata	int idtype;
994281198Stakawata	uint16_t *idp;
995290038Stakawata	int silent = 0;
996290038Stakawata
997107120Sjulian	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
998107120Sjulian	if (con->rx_pkt == NULL)
999107120Sjulian		return (ENOBUFS);
1000107120Sjulian
1001107120Sjulian	hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *);
1002107120Sjulian
1003107120Sjulian	/* Check channel */
1004281198Stakawata
1005281198Stakawata	if(hdr->dcid == NG_L2CAP_ATT_CID){
1006281198Stakawata		idtype = NG_L2CAP_L2CA_IDTYPE_ATT;
1007281198Stakawata		ch = ng_l2cap_chan_by_conhandle(l2cap, NG_L2CAP_ATT_CID,
1008281198Stakawata						con->con_handle);
1009281198Stakawata		/*
1010281198Stakawata		 * Here,ATT channel is distinguished by
1011281198Stakawata		 * connection handle
1012281198Stakawata		 */
1013290038Stakawata		hdr->dcid = con->con_handle;
1014290038Stakawata		silent = 1;
1015290038Stakawata	}else if(hdr->dcid == NG_L2CAP_SMP_CID){
1016290038Stakawata		idtype = NG_L2CAP_L2CA_IDTYPE_SMP;
1017290038Stakawata		ch = ng_l2cap_chan_by_conhandle(l2cap, NG_L2CAP_SMP_CID,
1018290038Stakawata						con->con_handle);
1019290038Stakawata		/*
1020290038Stakawata		 * Here,SMP channel is distinguished by
1021290038Stakawata		 * connection handle
1022290038Stakawata		 */
1023290038Stakawata		silent = 1;
1024281198Stakawata		hdr->dcid = con->con_handle;
1025281198Stakawata	}else{
1026281198Stakawata		idtype = (con->linktype==NG_HCI_LINK_ACL)?
1027281198Stakawata			NG_L2CAP_L2CA_IDTYPE_BREDR:
1028281198Stakawata			NG_L2CAP_L2CA_IDTYPE_LE;
1029281198Stakawata		ch = ng_l2cap_chan_by_scid(l2cap, hdr->dcid, idtype);
1030281198Stakawata	}
1031107120Sjulian	if (ch == NULL) {
1032290038Stakawata		if(!silent)
1033290038Stakawata			NG_L2CAP_ERR(
1034281198Stakawata"%s: %s - unexpected L2CAP data packet. Channel does not exist, cid=%d, idtype=%d\n",
1035281198Stakawata	__func__, NG_NODE_NAME(l2cap->node), hdr->dcid, idtype);
1036107120Sjulian		error = ENOENT;
1037107120Sjulian		goto drop;
1038107120Sjulian	}
1039107120Sjulian
1040107120Sjulian	/* Check channel state */
1041107120Sjulian	if (ch->state != NG_L2CAP_OPEN) {
1042107120Sjulian		NG_L2CAP_WARN(
1043107120Sjulian"%s: %s - unexpected L2CAP data packet. " \
1044107120Sjulian"Invalid channel state, cid=%d, state=%d\n",
1045107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->scid,
1046107120Sjulian			ch->state);
1047107120Sjulian		error = EHOSTDOWN; /* XXX not always - re-configuration */
1048107120Sjulian		goto drop;
1049107120Sjulian	}
1050107120Sjulian
1051107120Sjulian	/* Check payload size and channel's MTU */
1052107120Sjulian	if (hdr->length > ch->imtu) {
1053107120Sjulian		NG_L2CAP_ERR(
1054107120Sjulian"%s: %s - invalid L2CAP data packet. " \
1055107120Sjulian"Packet too big, length=%d, imtu=%d, cid=%d\n",
1056107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), hdr->length,
1057107120Sjulian			ch->imtu, ch->scid);
1058107120Sjulian		error = EMSGSIZE;
1059107120Sjulian		goto drop;
1060107120Sjulian	}
1061107120Sjulian
1062107120Sjulian	/*
1063107120Sjulian	 * If we got here then everything looks good and we can sent packet
1064107120Sjulian	 * to the upper layer protocol.
1065107120Sjulian	 */
1066107120Sjulian
1067107120Sjulian	/* Check if upstream hook is connected and valid */
1068107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1069107120Sjulian		NG_L2CAP_ERR(
1070107120Sjulian"%s: %s - unable to send L2CAP data packet. " \
1071107120Sjulian"Hook is not connected or valid, psm=%d\n",
1072107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
1073107120Sjulian		error = ENOTCONN;
1074107120Sjulian		goto drop;
1075107120Sjulian	}
1076281198Stakawata	M_PREPEND(con->rx_pkt, sizeof(uint16_t), M_NOWAIT);
1077281198Stakawata	if(con->rx_pkt == NULL)
1078281198Stakawata		goto drop;
1079281198Stakawata	idp = mtod(con->rx_pkt, uint16_t *);
1080281198Stakawata	*idp = idtype;
1081107120Sjulian
1082107120Sjulian	NG_SEND_DATA_ONLY(error, l2cap->l2c, con->rx_pkt);
1083107120Sjulian	con->rx_pkt = NULL;
1084107120Sjuliandrop:
1085107120Sjulian	NG_FREE_M(con->rx_pkt); /* checks for != NULL */
1086107120Sjulian
1087107120Sjulian	return (error);
1088107120Sjulian} /* ng_l2cap_receive */
1089107120Sjulian
1090107120Sjulian/*
1091107120Sjulian * Receive connectioless (multicast) packet from the lower layer protocol and
1092107120Sjulian * send it to the upper layer protocol
1093107120Sjulian */
1094107120Sjulian
1095107120Sjulianint
1096107120Sjulianng_l2cap_l2ca_clt_receive(ng_l2cap_con_p con)
1097107120Sjulian{
1098107120Sjulian	struct _clt_pkt {
1099107120Sjulian		ng_l2cap_hdr_t		 h;
1100107120Sjulian		ng_l2cap_clt_hdr_t	 c_h;
1101107120Sjulian	} __attribute__ ((packed))	*hdr = NULL;
1102107120Sjulian	ng_l2cap_p			 l2cap = con->l2cap;
1103107120Sjulian	int				 length, error = 0;
1104107120Sjulian
1105107120Sjulian	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
1106107120Sjulian	if (con->rx_pkt == NULL)
1107107120Sjulian		return (ENOBUFS);
1108107120Sjulian
1109107120Sjulian	hdr = mtod(con->rx_pkt, struct _clt_pkt *);
1110107120Sjulian
1111107120Sjulian	/* Check packet */
1112107120Sjulian	length = con->rx_pkt->m_pkthdr.len - sizeof(*hdr);
1113107120Sjulian	if (length < 0) {
1114107120Sjulian		NG_L2CAP_ERR(
1115107120Sjulian"%s: %s - invalid L2CAP CLT data packet. Packet too small, length=%d\n",
1116107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), length);
1117107120Sjulian		error = EMSGSIZE;
1118107120Sjulian		goto drop;
1119107120Sjulian	}
1120107120Sjulian
1121107120Sjulian	/* Check payload size against CLT MTU */
1122107120Sjulian	if (length > NG_L2CAP_MTU_DEFAULT) {
1123107120Sjulian		NG_L2CAP_ERR(
1124107120Sjulian"%s: %s - invalid L2CAP CLT data packet. Packet too big, length=%d, mtu=%d\n",
1125107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), length,
1126107120Sjulian			NG_L2CAP_MTU_DEFAULT);
1127107120Sjulian		error = EMSGSIZE;
1128107120Sjulian		goto drop;
1129107120Sjulian	}
1130107120Sjulian
1131107120Sjulian	hdr->c_h.psm = le16toh(hdr->c_h.psm);
1132107120Sjulian
1133107120Sjulian	/*
1134107120Sjulian	 * If we got here then everything looks good and we can sent packet
1135107120Sjulian	 * to the upper layer protocol.
1136107120Sjulian	 */
1137107120Sjulian
1138107120Sjulian	/* Select upstream hook based on PSM */
1139107120Sjulian	switch (hdr->c_h.psm) {
1140107120Sjulian	case NG_L2CAP_PSM_SDP:
1141107120Sjulian		if (l2cap->flags & NG_L2CAP_CLT_SDP_DISABLED)
1142107120Sjulian			goto drop;
1143107120Sjulian		break;
1144107120Sjulian
1145107120Sjulian	case NG_L2CAP_PSM_RFCOMM:
1146107120Sjulian		if (l2cap->flags & NG_L2CAP_CLT_RFCOMM_DISABLED)
1147107120Sjulian			goto drop;
1148107120Sjulian		break;
1149107120Sjulian
1150107120Sjulian	case NG_L2CAP_PSM_TCP:
1151107120Sjulian		if (l2cap->flags & NG_L2CAP_CLT_TCP_DISABLED)
1152107120Sjulian			goto drop;
1153107120Sjulian		break;
1154107120Sjulian        }
1155107120Sjulian
1156107120Sjulian	/* Check if upstream hook is connected and valid */
1157107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1158107120Sjulian		NG_L2CAP_ERR(
1159107120Sjulian"%s: %s - unable to send L2CAP CLT data packet. " \
1160107120Sjulian"Hook is not connected or valid, psm=%d\n",
1161107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), hdr->c_h.psm);
1162107120Sjulian		error = ENOTCONN;
1163107120Sjulian		goto drop;
1164107120Sjulian	}
1165107120Sjulian
1166107120Sjulian	NG_SEND_DATA_ONLY(error, l2cap->l2c, con->rx_pkt);
1167107120Sjulian	con->rx_pkt = NULL;
1168107120Sjuliandrop:
1169107120Sjulian	NG_FREE_M(con->rx_pkt); /* checks for != NULL */
1170107120Sjulian
1171107120Sjulian	return (error);
1172107120Sjulian} /* ng_l2cap_l2ca_clt_receive */
1173107120Sjulian
1174107120Sjulian/*
1175107120Sjulian * Send L2CA_QoSViolationInd to the upper layer protocol
1176107120Sjulian */
1177107120Sjulian
1178107120Sjulianint
1179107120Sjulianng_l2cap_l2ca_qos_ind(ng_l2cap_chan_p ch)
1180107120Sjulian{
1181107120Sjulian	ng_l2cap_p			 l2cap = ch->con->l2cap;
1182107120Sjulian	struct ng_mesg			*msg = NULL;
1183107120Sjulian	ng_l2cap_l2ca_qos_ind_ip	*ip = NULL;
1184107120Sjulian	int				 error = 0;
1185107120Sjulian
1186107120Sjulian	/* Check if upstream hook is connected and valid */
1187107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1188107120Sjulian		NG_L2CAP_ERR(
1189107120Sjulian"%s: %s - unable to send L2CA_QoSViolationInd message. " \
1190107120Sjulian"Hook is not connected or valid, psm=%d\n",
1191107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
1192107120Sjulian
1193107120Sjulian		return (ENOTCONN);
1194107120Sjulian	}
1195107120Sjulian
1196107120Sjulian	/* Create and send L2CA_QoSViolationInd message */
1197107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_QOS_IND,
1198107120Sjulian		sizeof(*ip), M_NOWAIT);
1199107120Sjulian	if (msg == NULL)
1200107120Sjulian		error = ENOMEM;
1201107120Sjulian	else {
1202107120Sjulian		ip = (ng_l2cap_l2ca_qos_ind_ip *)(msg->data);
1203107120Sjulian		bcopy(&ch->con->remote, &ip->bdaddr, sizeof(ip->bdaddr));
1204128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
1205107120Sjulian	}
1206107120Sjulian
1207107120Sjulian	return (error);
1208107120Sjulian} /* ng_l2cap_l2ca_qos_ind */
1209107120Sjulian
1210107120Sjulian/*
1211107120Sjulian * Process L2CA_Disconnect request from the upper layer protocol.
1212107120Sjulian */
1213107120Sjulian
1214107120Sjulianint
1215107120Sjulianng_l2cap_l2ca_discon_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
1216107120Sjulian{
1217107120Sjulian	ng_l2cap_l2ca_discon_ip	*ip = NULL;
1218107120Sjulian	ng_l2cap_chan_p		 ch = NULL;
1219107120Sjulian	ng_l2cap_cmd_p		 cmd = NULL;
1220107120Sjulian	int			 error = 0;
1221107120Sjulian
1222107120Sjulian	/* Check message */
1223107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
1224107120Sjulian		NG_L2CAP_ALERT(
1225107120Sjulian"%s: %s - invalid L2CA_Disconnect request message size, size=%d\n",
1226107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
1227107120Sjulian			msg->header.arglen);
1228107120Sjulian		error = EMSGSIZE;
1229107120Sjulian		goto out;
1230107120Sjulian	}
1231107120Sjulian
1232107120Sjulian	ip = (ng_l2cap_l2ca_discon_ip *)(msg->data);
1233107120Sjulian
1234281198Stakawata
1235281198Stakawata	if(ip->idtype == NG_L2CAP_L2CA_IDTYPE_ATT){
1236281198Stakawata		/* Don't send Disconnect request on L2CAP Layer*/
1237281198Stakawata		ch = ng_l2cap_chan_by_conhandle(l2cap, NG_L2CAP_ATT_CID,
1238281198Stakawata			ip->lcid);
1239281198Stakawata
1240281198Stakawata		if(ch != NULL){
1241281198Stakawata			ng_l2cap_free_chan(ch);
1242281198Stakawata		}else{
1243281198Stakawata		NG_L2CAP_ERR(
1244281198Stakawata"%s: %s - unexpected L2CA_Disconnect request message. " \
1245281198Stakawata"Channel does not exist, conhandle=%d\n",
1246281198Stakawata			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
1247281198Stakawata			error = EINVAL;
1248281198Stakawata		}
1249281198Stakawata		goto out;
1250290038Stakawata	}else if(ip->idtype == NG_L2CAP_L2CA_IDTYPE_SMP){
1251290038Stakawata		/* Don't send Disconnect request on L2CAP Layer*/
1252290038Stakawata		ch = ng_l2cap_chan_by_conhandle(l2cap, NG_L2CAP_SMP_CID,
1253290038Stakawata			ip->lcid);
1254290038Stakawata
1255290038Stakawata		if(ch != NULL){
1256290038Stakawata			ng_l2cap_free_chan(ch);
1257290038Stakawata		}else{
1258290038Stakawata		NG_L2CAP_ERR(
1259290038Stakawata"%s: %s - unexpected L2CA_Disconnect request message. " \
1260290038Stakawata"Channel does not exist, conhandle=%d\n",
1261290038Stakawata			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
1262290038Stakawata			error = EINVAL;
1263290038Stakawata		}
1264290038Stakawata		goto out;
1265281198Stakawata	}else{
1266281198Stakawata		/* Check if we have this channel */
1267281198Stakawata		ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid, ip->idtype);
1268281198Stakawata	}
1269107120Sjulian	if (ch == NULL) {
1270107120Sjulian		NG_L2CAP_ERR(
1271107120Sjulian"%s: %s - unexpected L2CA_Disconnect request message. " \
1272107120Sjulian"Channel does not exist, lcid=%d\n",
1273107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
1274107120Sjulian		error = ENOENT;
1275107120Sjulian		goto out;
1276107120Sjulian	}
1277107120Sjulian
1278107120Sjulian	/* Check channel state */
1279107120Sjulian	if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN &&
1280107120Sjulian	    ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
1281107120Sjulian		NG_L2CAP_ERR(
1282107120Sjulian"%s: %s - unexpected L2CA_Disconnect request message. " \
1283107120Sjulian"Invalid channel state, state=%d, lcid=%d\n",
1284107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->state,
1285107120Sjulian			ch->scid);
1286107120Sjulian		error = EINVAL;
1287107120Sjulian		goto out;
1288107120Sjulian	}
1289107120Sjulian
1290107120Sjulian	/* Create and send L2CAP_DisconReq message */
1291107120Sjulian	cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(ch->con),
1292107120Sjulian			NG_L2CAP_DISCON_REQ, msg->header.token);
1293107120Sjulian	if (cmd == NULL) {
1294107120Sjulian		ng_l2cap_free_chan(ch);
1295107120Sjulian		error = ENOMEM;
1296107120Sjulian		goto out;
1297107120Sjulian	}
1298107120Sjulian
1299107120Sjulian	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
1300107120Sjulian		ng_l2cap_free_chan(ch);
1301107120Sjulian		ng_l2cap_free_cmd(cmd);
1302107120Sjulian		error = EIO;
1303107120Sjulian		goto out;
1304107120Sjulian	}
1305107120Sjulian
1306107120Sjulian	_ng_l2cap_discon_req(cmd->aux, cmd->ident, ch->dcid, ch->scid);
1307107120Sjulian	if (cmd->aux == NULL) {
1308107120Sjulian		ng_l2cap_free_chan(ch);
1309107120Sjulian		ng_l2cap_free_cmd(cmd);
1310107120Sjulian		error = ENOBUFS;
1311107120Sjulian		goto out;
1312107120Sjulian	}
1313107120Sjulian
1314107120Sjulian	ch->state = NG_L2CAP_W4_L2CAP_DISCON_RSP;
1315107120Sjulian
1316107120Sjulian	/* Link command to the queue */
1317107120Sjulian	ng_l2cap_link_cmd(ch->con, cmd);
1318107120Sjulian	ng_l2cap_lp_deliver(ch->con);
1319107120Sjulianout:
1320107120Sjulian	return (error);
1321107120Sjulian} /* ng_l2cap_l2ca_discon_req */
1322107120Sjulian
1323107120Sjulian/*
1324107120Sjulian * Send L2CA_Disconnect response to the upper layer protocol
1325107120Sjulian */
1326107120Sjulian
1327107120Sjulianint
1328107120Sjulianng_l2cap_l2ca_discon_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
1329107120Sjulian{
1330107120Sjulian	ng_l2cap_p		 l2cap = ch->con->l2cap;
1331107120Sjulian	struct ng_mesg		*msg = NULL;
1332107120Sjulian	ng_l2cap_l2ca_discon_op	*op = NULL;
1333107120Sjulian	int			 error = 0;
1334107120Sjulian
1335107120Sjulian	/* Check if upstream hook is connected and valid */
1336107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1337107120Sjulian		NG_L2CAP_ERR(
1338107120Sjulian"%s: %s - unable to send L2CA_Disconnect response message. " \
1339107120Sjulian"Hook is not connected or valid, psm=%d\n",
1340107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
1341107120Sjulian
1342107120Sjulian		return (ENOTCONN);
1343107120Sjulian	}
1344107120Sjulian
1345107120Sjulian	/* Create and send L2CA_Disconnect response message */
1346107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON,
1347107120Sjulian		sizeof(*op), M_NOWAIT);
1348107120Sjulian	if (msg == NULL)
1349107120Sjulian		error = ENOMEM;
1350107120Sjulian	else {
1351107120Sjulian		msg->header.token = token;
1352107120Sjulian		msg->header.flags |= NGF_RESP;
1353107120Sjulian
1354107120Sjulian		op = (ng_l2cap_l2ca_discon_op *)(msg->data);
1355107120Sjulian		op->result = result;
1356107120Sjulian
1357128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
1358107120Sjulian	}
1359107120Sjulian
1360107120Sjulian	return (error);
1361107120Sjulian} /* ng_l2cap_l2ca_discon_rsp */
1362107120Sjulian
1363107120Sjulian/*
1364107120Sjulian * Send L2CA_DisconnectInd message to the upper layer protocol.
1365107120Sjulian */
1366107120Sjulian
1367107120Sjulianint
1368107120Sjulianng_l2cap_l2ca_discon_ind(ng_l2cap_chan_p ch)
1369107120Sjulian{
1370107120Sjulian	ng_l2cap_p			 l2cap = ch->con->l2cap;
1371107120Sjulian	struct ng_mesg			*msg = NULL;
1372107120Sjulian	ng_l2cap_l2ca_discon_ind_ip	*ip = NULL;
1373107120Sjulian	int				 error = 0;
1374107120Sjulian
1375107120Sjulian	/* Check if upstream hook is connected and valid */
1376107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1377107120Sjulian		NG_L2CAP_ERR(
1378107120Sjulian"%s: %s - unable to send L2CA_DisconnectInd message. " \
1379107120Sjulian"Hook is not connected or valid, psm=%d\n",
1380107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
1381107120Sjulian
1382107120Sjulian		return (ENOTCONN);
1383107120Sjulian	}
1384107120Sjulian
1385107120Sjulian	/* Create and send L2CA_DisconnectInd message */
1386107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON_IND,
1387107120Sjulian		sizeof(*ip), M_NOWAIT);
1388107120Sjulian	if (msg == NULL)
1389107120Sjulian		error = ENOMEM;
1390107120Sjulian	else {
1391107120Sjulian		ip = (ng_l2cap_l2ca_discon_ind_ip *)(msg->data);
1392301558Stakawata		ip->idtype = ch->idtype;
1393301558Stakawata		if(ch->idtype == NG_L2CAP_L2CA_IDTYPE_ATT||
1394301558Stakawata		   ch->idtype == NG_L2CAP_L2CA_IDTYPE_SMP)
1395301558Stakawata			ip->lcid = ch->con->con_handle;
1396301558Stakawata		else
1397301558Stakawata			ip->lcid = ch->scid;
1398301558Stakawata
1399128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
1400107120Sjulian	}
1401107120Sjulian
1402107120Sjulian	return (error);
1403107120Sjulian} /* ng_l2cap_l2ca_discon_ind */
1404107120Sjulian
1405107120Sjulian/*
1406107120Sjulian * Process L2CA_GroupCreate request from the upper layer protocol.
1407107120Sjulian * XXX FIXME
1408107120Sjulian */
1409107120Sjulian
1410107120Sjulianint
1411107120Sjulianng_l2cap_l2ca_grp_create(ng_l2cap_p l2cap, struct ng_mesg *msg)
1412107120Sjulian{
1413107120Sjulian	return (ENOTSUP);
1414107120Sjulian} /* ng_l2cap_l2ca_grp_create */
1415107120Sjulian
1416107120Sjulian/*
1417107120Sjulian * Process L2CA_GroupClose request from the upper layer protocol
1418107120Sjulian * XXX FIXME
1419107120Sjulian */
1420107120Sjulian
1421107120Sjulianint
1422107120Sjulianng_l2cap_l2ca_grp_close(ng_l2cap_p l2cap, struct ng_mesg *msg)
1423107120Sjulian{
1424107120Sjulian	return (ENOTSUP);
1425107120Sjulian} /* ng_l2cap_l2ca_grp_close */
1426107120Sjulian
1427107120Sjulian/*
1428107120Sjulian * Process L2CA_GroupAddMember request from the upper layer protocol.
1429107120Sjulian * XXX FIXME
1430107120Sjulian */
1431107120Sjulian
1432107120Sjulianint
1433107120Sjulianng_l2cap_l2ca_grp_add_member_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
1434107120Sjulian{
1435107120Sjulian	return (ENOTSUP);
1436107120Sjulian} /* ng_l2cap_l2ca_grp_add_member_req */
1437107120Sjulian
1438107120Sjulian/*
1439107120Sjulian * Send L2CA_GroupAddMember response to the upper layer protocol.
1440107120Sjulian * XXX FIXME
1441107120Sjulian */
1442107120Sjulian
1443107120Sjulianint
1444107120Sjulianng_l2cap_l2ca_grp_add_member_rsp(ng_l2cap_chan_p ch, u_int32_t token,
1445107120Sjulian		u_int16_t result)
1446107120Sjulian{
1447107120Sjulian	return (0);
1448107120Sjulian} /* ng_l2cap_l2ca_grp_add_member_rsp */
1449107120Sjulian
1450107120Sjulian/*
1451107120Sjulian * Process L2CA_GroupDeleteMember request from the upper layer protocol
1452107120Sjulian * XXX FIXME
1453107120Sjulian */
1454107120Sjulian
1455107120Sjulianint
1456107120Sjulianng_l2cap_l2ca_grp_rem_member(ng_l2cap_p l2cap, struct ng_mesg *msg)
1457107120Sjulian{
1458107120Sjulian	return (ENOTSUP);
1459107120Sjulian} /* ng_l2cap_l2ca_grp_rem_member */
1460107120Sjulian
1461107120Sjulian/*
1462107120Sjulian * Process L2CA_GroupGetMembers request from the upper layer protocol
1463107120Sjulian * XXX FIXME
1464107120Sjulian */
1465107120Sjulian
1466107120Sjulianint
1467107120Sjulianng_l2cap_l2ca_grp_get_members(ng_l2cap_p l2cap, struct ng_mesg *msg)
1468107120Sjulian{
1469107120Sjulian	return (ENOTSUP);
1470107120Sjulian} /* ng_l2cap_l2ca_grp_get_members */
1471107120Sjulian
1472107120Sjulian/*
1473107120Sjulian * Process L2CA_Ping request from the upper layer protocol
1474107120Sjulian */
1475107120Sjulian
1476107120Sjulianint
1477107120Sjulianng_l2cap_l2ca_ping_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
1478107120Sjulian{
1479107120Sjulian	ng_l2cap_l2ca_ping_ip	*ip = NULL;
1480107120Sjulian	ng_l2cap_con_p		 con = NULL;
1481107120Sjulian	ng_l2cap_cmd_p		 cmd = NULL;
1482107120Sjulian	int			 error = 0;
1483107120Sjulian
1484107120Sjulian	/* Verify message */
1485107120Sjulian	if (msg->header.arglen < sizeof(*ip)) {
1486107120Sjulian		NG_L2CAP_ALERT(
1487107120Sjulian"%s: %s - invalid L2CA_Ping request message size, size=%d\n",
1488107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
1489107120Sjulian			msg->header.arglen);
1490107120Sjulian		error = EMSGSIZE;
1491107120Sjulian		goto out;
1492107120Sjulian	}
1493107120Sjulian
1494107120Sjulian	ip = (ng_l2cap_l2ca_ping_ip *)(msg->data);
1495107120Sjulian	if (ip->echo_size > NG_L2CAP_MAX_ECHO_SIZE) {
1496107120Sjulian		NG_L2CAP_WARN(
1497107120Sjulian"%s: %s - invalid L2CA_Ping request. Echo size is too big, echo_size=%d\n",
1498107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ip->echo_size);
1499107120Sjulian		error = EMSGSIZE;
1500107120Sjulian		goto out;
1501107120Sjulian	}
1502107120Sjulian
1503107120Sjulian	/* Check if we have connection to the unit */
1504281198Stakawata	con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr, NG_HCI_LINK_ACL);
1505107120Sjulian	if (con == NULL) {
1506107120Sjulian		/* Submit LP_ConnectReq to the lower layer */
1507281198Stakawata	  error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr, NG_HCI_LINK_ACL);
1508107120Sjulian		if (error != 0) {
1509107120Sjulian			NG_L2CAP_ERR(
1510107120Sjulian"%s: %s - unable to send LP_ConnectReq message, error=%d\n",
1511107120Sjulian				__func__, NG_NODE_NAME(l2cap->node), error);
1512107120Sjulian			goto out;
1513107120Sjulian		}
1514107120Sjulian
1515107120Sjulian		/* This should not fail */
1516281198Stakawata		con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr, NG_HCI_LINK_ACL);
1517107120Sjulian		KASSERT((con != NULL),
1518107120Sjulian("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node)));
1519107120Sjulian	}
1520107120Sjulian
1521107120Sjulian	/* Create L2CAP command descriptor */
1522107120Sjulian	cmd = ng_l2cap_new_cmd(con, NULL, ng_l2cap_get_ident(con),
1523107120Sjulian			NG_L2CAP_ECHO_REQ, msg->header.token);
1524107120Sjulian	if (cmd == NULL) {
1525107120Sjulian		error = ENOMEM;
1526107120Sjulian		goto out;
1527107120Sjulian	}
1528107120Sjulian
1529107120Sjulian	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
1530107120Sjulian		ng_l2cap_free_cmd(cmd);
1531107120Sjulian                error = EIO;
1532107120Sjulian		goto out;
1533107120Sjulian	}
1534107120Sjulian
1535107120Sjulian	/* Create L2CAP command packet */
1536107120Sjulian	_ng_l2cap_echo_req(cmd->aux, cmd->ident,
1537107120Sjulian			msg->data + sizeof(*ip), ip->echo_size);
1538107120Sjulian	if (cmd->aux == NULL) {
1539107120Sjulian		ng_l2cap_free_cmd(cmd);
1540107120Sjulian                error = ENOBUFS;
1541107120Sjulian		goto out;
1542107120Sjulian	}
1543107120Sjulian
1544107120Sjulian        /* Link command to the queue */
1545107120Sjulian        ng_l2cap_link_cmd(con, cmd);
1546107120Sjulian	ng_l2cap_lp_deliver(con);
1547107120Sjulianout:
1548107120Sjulian	return (error);
1549107120Sjulian} /* ng_l2cap_l2ca_ping_req */
1550107120Sjulian
1551107120Sjulian/*
1552107120Sjulian * Send L2CA_Ping response to the upper layer protocol
1553107120Sjulian */
1554107120Sjulian
1555107120Sjulianint
1556107120Sjulianng_l2cap_l2ca_ping_rsp(ng_l2cap_con_p con, u_int32_t token, u_int16_t result,
1557107120Sjulian		struct mbuf *data)
1558107120Sjulian{
1559107120Sjulian	ng_l2cap_p		 l2cap = con->l2cap;
1560107120Sjulian	struct ng_mesg		*msg = NULL;
1561107120Sjulian	ng_l2cap_l2ca_ping_op	*op = NULL;
1562107120Sjulian	int			 error = 0, size = 0;
1563107120Sjulian
1564107120Sjulian	/* Check if control hook is connected and valid */
1565107120Sjulian	if (l2cap->ctl == NULL || NG_HOOK_NOT_VALID(l2cap->ctl)) {
1566107120Sjulian		NG_L2CAP_WARN(
1567107120Sjulian"%s: %s - unable to send L2CA_Ping response message. " \
1568107120Sjulian"Hook is not connected or valid\n",
1569107120Sjulian			__func__, NG_NODE_NAME(l2cap->node));
1570107120Sjulian		error = ENOTCONN;
1571107120Sjulian		goto out;
1572107120Sjulian	}
1573107120Sjulian
1574107120Sjulian	size = (data == NULL)? 0 : data->m_pkthdr.len;
1575107120Sjulian
1576107120Sjulian	/* Create and send L2CA_Ping response message */
1577107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_PING,
1578107120Sjulian		sizeof(*op) + size, M_NOWAIT);
1579107120Sjulian	if (msg == NULL)
1580107120Sjulian		error = ENOMEM;
1581107120Sjulian	else {
1582107120Sjulian		msg->header.token = token;
1583107120Sjulian		msg->header.flags |= NGF_RESP;
1584107120Sjulian
1585107120Sjulian		op = (ng_l2cap_l2ca_ping_op *)(msg->data);
1586107120Sjulian		op->result = result;
1587107120Sjulian		bcopy(&con->remote, &op->bdaddr, sizeof(op->bdaddr));
1588107120Sjulian		if (data != NULL && size > 0) {
1589107120Sjulian			op->echo_size = size;
1590107120Sjulian			m_copydata(data, 0, size, (caddr_t) op + sizeof(*op));
1591107120Sjulian		}
1592107120Sjulian
1593128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, 0);
1594107120Sjulian	}
1595107120Sjulianout:
1596107120Sjulian	NG_FREE_M(data);
1597107120Sjulian
1598107120Sjulian	return (error);
1599107120Sjulian} /* ng_l2cap_l2ca_ping_rsp */
1600107120Sjulian
1601107120Sjulian/*
1602107120Sjulian * Process L2CA_GetInfo request from the upper layer protocol
1603107120Sjulian */
1604107120Sjulian
1605107120Sjulianint
1606107120Sjulianng_l2cap_l2ca_get_info_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
1607107120Sjulian{
1608107120Sjulian	ng_l2cap_l2ca_get_info_ip	*ip = NULL;
1609107120Sjulian	ng_l2cap_con_p			 con = NULL;
1610107120Sjulian	ng_l2cap_cmd_p			 cmd = NULL;
1611107120Sjulian	int				 error = 0;
1612107120Sjulian
1613107120Sjulian	/* Verify message */
1614107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
1615107120Sjulian		NG_L2CAP_ALERT(
1616107120Sjulian"%s: %s - invalid L2CA_GetInfo request message size, size=%d\n",
1617107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
1618107120Sjulian			msg->header.arglen);
1619107120Sjulian		error = EMSGSIZE;
1620107120Sjulian		goto out;
1621107120Sjulian	}
1622107120Sjulian
1623107120Sjulian	ip = (ng_l2cap_l2ca_get_info_ip *)(msg->data);
1624107120Sjulian
1625107120Sjulian	/* Check if we have connection to the unit */
1626281198Stakawata	con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr,ip->linktype);
1627107120Sjulian	if (con == NULL) {
1628107120Sjulian		/* Submit LP_ConnectReq to the lower layer */
1629281198Stakawata		error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr,ip->linktype);
1630107120Sjulian		if (error != 0) {
1631107120Sjulian			NG_L2CAP_ERR(
1632107120Sjulian"%s: %s - unable to send LP_ConnectReq message, error=%d\n",
1633107120Sjulian				__func__, NG_NODE_NAME(l2cap->node), error);
1634107120Sjulian			goto out;
1635107120Sjulian		}
1636107120Sjulian
1637107120Sjulian		/* This should not fail */
1638281198Stakawata		con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr, ip->linktype);
1639107120Sjulian		KASSERT((con != NULL),
1640107120Sjulian("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node)));
1641107120Sjulian	}
1642107120Sjulian
1643107120Sjulian	/* Create L2CAP command descriptor */
1644107120Sjulian	cmd = ng_l2cap_new_cmd(con, NULL, ng_l2cap_get_ident(con),
1645107120Sjulian			NG_L2CAP_INFO_REQ, msg->header.token);
1646107120Sjulian	if (cmd == NULL) {
1647107120Sjulian		error = ENOMEM;
1648107120Sjulian		goto out;
1649107120Sjulian	}
1650107120Sjulian
1651107120Sjulian	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
1652107120Sjulian		ng_l2cap_free_cmd(cmd);
1653107120Sjulian		error = EIO;
1654107120Sjulian		goto out;
1655107120Sjulian	}
1656107120Sjulian
1657107120Sjulian	/* Create L2CAP command packet */
1658107120Sjulian	_ng_l2cap_info_req(cmd->aux, cmd->ident, ip->info_type);
1659107120Sjulian	if (cmd->aux == NULL) {
1660107120Sjulian		ng_l2cap_free_cmd(cmd);
1661107120Sjulian		error = ENOBUFS;
1662107120Sjulian		goto out;
1663107120Sjulian	}
1664107120Sjulian
1665107120Sjulian        /* Link command to the queue */
1666107120Sjulian	ng_l2cap_link_cmd(con, cmd);
1667107120Sjulian	ng_l2cap_lp_deliver(con);
1668107120Sjulianout:
1669107120Sjulian	return (error);
1670107120Sjulian} /* ng_l2cap_l2ca_get_info_req */
1671107120Sjulian
1672107120Sjulian/*
1673107120Sjulian * Send L2CA_GetInfo response to the upper layer protocol
1674107120Sjulian */
1675107120Sjulian
1676107120Sjulianint
1677107120Sjulianng_l2cap_l2ca_get_info_rsp(ng_l2cap_con_p con, u_int32_t token,
1678107120Sjulian		u_int16_t result, struct mbuf *data)
1679107120Sjulian{
1680107120Sjulian	ng_l2cap_p			 l2cap = con->l2cap;
1681107120Sjulian	struct ng_mesg			*msg = NULL;
1682107120Sjulian	ng_l2cap_l2ca_get_info_op	*op = NULL;
1683107120Sjulian	int				 error = 0, size;
1684107120Sjulian
1685107120Sjulian	/* Check if control hook is connected and valid */
1686107120Sjulian	if (l2cap->ctl == NULL || NG_HOOK_NOT_VALID(l2cap->ctl)) {
1687107120Sjulian		NG_L2CAP_WARN(
1688107120Sjulian"%s: %s - unable to send L2CA_GetInfo response message. " \
1689107120Sjulian"Hook is not connected or valid\n",
1690107120Sjulian			__func__, NG_NODE_NAME(l2cap->node));
1691107120Sjulian		error = ENOTCONN;
1692107120Sjulian		goto out;
1693107120Sjulian	}
1694107120Sjulian
1695107120Sjulian	size = (data == NULL)? 0 : data->m_pkthdr.len;
1696107120Sjulian
1697107120Sjulian	/* Create and send L2CA_GetInfo response message */
1698107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_GET_INFO,
1699107120Sjulian		sizeof(*op) + size, M_NOWAIT);
1700107120Sjulian	if (msg == NULL)
1701107120Sjulian		error = ENOMEM;
1702107120Sjulian	else {
1703107120Sjulian		msg->header.token = token;
1704107120Sjulian		msg->header.flags |= NGF_RESP;
1705107120Sjulian
1706107120Sjulian		op = (ng_l2cap_l2ca_get_info_op *)(msg->data);
1707107120Sjulian		op->result = result;
1708107120Sjulian		if (data != NULL && size > 0) {
1709107120Sjulian			op->info_size = size;
1710107120Sjulian			m_copydata(data, 0, size, (caddr_t) op + sizeof(*op));
1711107120Sjulian		}
1712107120Sjulian
1713128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, 0);
1714107120Sjulian	}
1715107120Sjulianout:
1716107120Sjulian	NG_FREE_M(data);
1717107120Sjulian
1718107120Sjulian	return (error);
1719107120Sjulian} /* ng_l2cap_l2ca_get_info_rsp */
1720107120Sjulian
1721107120Sjulian/*
1722107120Sjulian * Process L2CA_EnableCLT message from the upper layer protocol
1723107120Sjulian * XXX convert to NGN_L2CAP_NODE_SET_FLAGS?
1724107120Sjulian */
1725107120Sjulian
1726107120Sjulianint
1727107120Sjulianng_l2cap_l2ca_enable_clt(ng_l2cap_p l2cap, struct ng_mesg *msg)
1728107120Sjulian{
1729107120Sjulian	ng_l2cap_l2ca_enable_clt_ip	*ip = NULL;
1730107120Sjulian	int				 error = 0;
1731107120Sjulian#if 0
1732107120Sjulian *	ng_l2cap_l2ca_enable_clt_op	*op = NULL;
1733107120Sjulian *	u_int16_t			 result;
1734107120Sjulian * 	u_int32_t			 token;
1735107120Sjulian#endif
1736107120Sjulian
1737107120Sjulian	/* Check message */
1738107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
1739107120Sjulian		NG_L2CAP_ALERT(
1740107120Sjulian"%s: %s - invalid L2CA_EnableCLT message size, size=%d\n",
1741107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
1742107120Sjulian			msg->header.arglen);
1743107120Sjulian
1744107120Sjulian		return (EMSGSIZE);
1745107120Sjulian	}
1746107120Sjulian
1747107120Sjulian	/* Process request */
1748107120Sjulian	ip = (ng_l2cap_l2ca_enable_clt_ip *) (msg->data);
1749107120Sjulian#if 0
1750107120Sjulian *	result = NG_L2CAP_SUCCESS;
1751107120Sjulian#endif
1752107120Sjulian
1753107120Sjulian	switch (ip->psm)
1754107120Sjulian	{
1755107120Sjulian	case 0:
1756107120Sjulian		/* Special case: disable/enable all PSM */
1757107120Sjulian		if (ip->enable)
1758107120Sjulian			l2cap->flags &= ~(NG_L2CAP_CLT_SDP_DISABLED    |
1759107120Sjulian					  NG_L2CAP_CLT_RFCOMM_DISABLED |
1760107120Sjulian					  NG_L2CAP_CLT_TCP_DISABLED);
1761107120Sjulian		else
1762107120Sjulian			l2cap->flags |= (NG_L2CAP_CLT_SDP_DISABLED    |
1763107120Sjulian					 NG_L2CAP_CLT_RFCOMM_DISABLED |
1764107120Sjulian					 NG_L2CAP_CLT_TCP_DISABLED);
1765107120Sjulian		break;
1766107120Sjulian
1767107120Sjulian	case NG_L2CAP_PSM_SDP:
1768107120Sjulian		if (ip->enable)
1769107120Sjulian			l2cap->flags &= ~NG_L2CAP_CLT_SDP_DISABLED;
1770107120Sjulian		else
1771107120Sjulian			l2cap->flags |= NG_L2CAP_CLT_SDP_DISABLED;
1772107120Sjulian		break;
1773107120Sjulian
1774107120Sjulian	case NG_L2CAP_PSM_RFCOMM:
1775107120Sjulian		if (ip->enable)
1776107120Sjulian			l2cap->flags &= ~NG_L2CAP_CLT_RFCOMM_DISABLED;
1777107120Sjulian		else
1778107120Sjulian			l2cap->flags |= NG_L2CAP_CLT_RFCOMM_DISABLED;
1779107120Sjulian		break;
1780107120Sjulian
1781107120Sjulian	case NG_L2CAP_PSM_TCP:
1782107120Sjulian		if (ip->enable)
1783107120Sjulian			l2cap->flags &= ~NG_L2CAP_CLT_TCP_DISABLED;
1784107120Sjulian		else
1785107120Sjulian			l2cap->flags |= NG_L2CAP_CLT_TCP_DISABLED;
1786107120Sjulian		break;
1787107120Sjulian
1788107120Sjulian	default:
1789107120Sjulian		NG_L2CAP_ERR(
1790107120Sjulian"%s: %s - unsupported PSM=%d\n", __func__, NG_NODE_NAME(l2cap->node), ip->psm);
1791107120Sjulian#if 0
1792107120Sjulian *		result = NG_L2CAP_PSM_NOT_SUPPORTED;
1793107120Sjulian#endif
1794107120Sjulian		error = ENOTSUP;
1795107120Sjulian		break;
1796107120Sjulian	}
1797107120Sjulian
1798107120Sjulian#if 0
1799107120Sjulian *	/* Create and send response message */
1800107120Sjulian * 	token = msg->header.token;
1801107120Sjulian * 	NG_FREE_MSG(msg);
1802107120Sjulian * 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_ENABLE_CLT,
1803107120Sjulian * 		sizeof(*op), M_NOWAIT);
1804107120Sjulian * 	if (msg == NULL)
1805107120Sjulian * 		error = ENOMEM;
1806107120Sjulian * 	else {
1807107120Sjulian * 		msg->header.token = token;
1808107120Sjulian * 		msg->header.flags |= NGF_RESP;
1809107120Sjulian *
1810107120Sjulian * 		op = (ng_l2cap_l2ca_enable_clt_op *)(msg->data);
1811107120Sjulian * 		op->result = result;
1812107120Sjulian * 	}
1813107120Sjulian *
1814107120Sjulian * 	/* Send response to control hook */
1815107120Sjulian * 	if (l2cap->ctl != NULL && NG_HOOK_IS_VALID(l2cap->ctl))
1816128076Semax * 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, 0);
1817107120Sjulian#endif
1818107120Sjulian
1819107120Sjulian	return (error);
1820107120Sjulian} /* ng_l2cap_l2ca_enable_clt */
1821107120Sjulian
1822