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 */
84107120Sjulian	con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
85107120Sjulian	if (con == NULL) {
86107120Sjulian		/* Submit LP_ConnectReq to the lower layer */
87107120Sjulian		error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr);
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 */
96107120Sjulian		con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
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
106107120Sjulian	ch = ng_l2cap_new_chan(l2cap, con, ip->psm);
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 */
129107120Sjulian	_ng_l2cap_con_req(cmd->aux, cmd->ident, ch->psm, ch->scid);
130107120Sjulian	if (cmd->aux == NULL) {
131107120Sjulian		ng_l2cap_free_cmd(cmd);
132107120Sjulian		ng_l2cap_free_chan(ch);
133107120Sjulian		error = ENOBUFS;
134107120Sjulian		goto out;
135107120Sjulian	}
136107120Sjulian
137107120Sjulian	ch->state = NG_L2CAP_W4_L2CAP_CON_RSP;
138107120Sjulian
139107120Sjulian	/* Link command to the queue */
140107120Sjulian	ng_l2cap_link_cmd(ch->con, cmd);
141107120Sjulian	ng_l2cap_lp_deliver(ch->con);
142107120Sjulianout:
143107120Sjulian	return (error);
144107120Sjulian} /* ng_l2cap_l2ca_con_req */
145107120Sjulian
146107120Sjulian/*
147107120Sjulian * Send L2CA_Connect response to the upper layer protocol.
148107120Sjulian */
149107120Sjulian
150107120Sjulianint
151107120Sjulianng_l2cap_l2ca_con_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result,
152107120Sjulian		u_int16_t status)
153107120Sjulian{
154107120Sjulian	ng_l2cap_p		 l2cap = ch->con->l2cap;
155107120Sjulian	struct ng_mesg		*msg = NULL;
156107120Sjulian	ng_l2cap_l2ca_con_op	*op = NULL;
157107120Sjulian	int			 error = 0;
158107120Sjulian
159107120Sjulian	/* Check if upstream hook is connected and valid */
160107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
161107120Sjulian		NG_L2CAP_ERR(
162107120Sjulian"%s: %s - unable to send L2CA_Connect response message. " \
163107120Sjulian"Hook is not connected or valid, psm=%d\n",
164107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
165107120Sjulian
166107120Sjulian		return (ENOTCONN);
167107120Sjulian	}
168107120Sjulian
169107120Sjulian	/* Create and send L2CA_Connect response message */
170107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON,
171107120Sjulian		sizeof(*op), M_NOWAIT);
172107120Sjulian	if (msg == NULL)
173107120Sjulian		error = ENOMEM;
174107120Sjulian	else {
175107120Sjulian		msg->header.token = token;
176107120Sjulian		msg->header.flags |= NGF_RESP;
177107120Sjulian
178107120Sjulian		op = (ng_l2cap_l2ca_con_op *)(msg->data);
179107120Sjulian
180107120Sjulian		/*
181107120Sjulian		 * XXX Spec. says we should only populate LCID when result == 0
182107120Sjulian		 * What about PENDING? What the heck, for now always populate
183107120Sjulian		 * LCID :)
184107120Sjulian		 */
185107120Sjulian
186107120Sjulian		op->lcid = ch->scid;
187107120Sjulian		op->result = result;
188107120Sjulian		op->status = status;
189107120Sjulian
190128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
191107120Sjulian	}
192107120Sjulian
193107120Sjulian	return (error);
194107120Sjulian} /* ng_l2cap_l2ca_con_rsp */
195107120Sjulian
196107120Sjulian/*
197107120Sjulian * Process L2CA_ConnectRsp request from the upper layer protocol.
198107120Sjulian */
199107120Sjulian
200107120Sjulianint
201107120Sjulianng_l2cap_l2ca_con_rsp_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
202107120Sjulian{
203107120Sjulian	ng_l2cap_l2ca_con_rsp_ip	*ip = NULL;
204107120Sjulian	ng_l2cap_con_p			 con = NULL;
205107120Sjulian	ng_l2cap_chan_p			 ch = NULL;
206107120Sjulian	ng_l2cap_cmd_p			 cmd = NULL;
207107120Sjulian	u_int16_t			 dcid;
208107120Sjulian	int				 error = 0;
209107120Sjulian
210107120Sjulian	/* Check message */
211107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
212107120Sjulian		NG_L2CAP_ALERT(
213107120Sjulian"%s: %s - invalid L2CA_ConnectRsp request message size, size=%d\n",
214107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
215107120Sjulian			msg->header.arglen);
216107120Sjulian		error = EMSGSIZE;
217107120Sjulian		goto out;
218107120Sjulian	}
219107120Sjulian
220107120Sjulian	ip = (ng_l2cap_l2ca_con_rsp_ip *)(msg->data);
221107120Sjulian
222107120Sjulian	/* Check if we have this channel */
223107120Sjulian	ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid);
224107120Sjulian	if (ch == NULL) {
225107120Sjulian		NG_L2CAP_ALERT(
226107120Sjulian"%s: %s - unexpected L2CA_ConnectRsp request message. " \
227107120Sjulian"Channel does not exist, lcid=%d\n",
228107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
229107120Sjulian		error = ENOENT;
230107120Sjulian		goto out;
231107120Sjulian	}
232107120Sjulian
233107120Sjulian	/* Check channel state */
234107120Sjulian	if (ch->state != NG_L2CAP_W4_L2CA_CON_RSP) {
235107120Sjulian		NG_L2CAP_ERR(
236107120Sjulian"%s: %s - unexpected L2CA_ConnectRsp request message. " \
237107120Sjulian"Invalid channel state, state=%d, lcid=%d\n",
238107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->state,
239107120Sjulian			ip->lcid);
240107120Sjulian		error = EINVAL;
241107120Sjulian		goto out;
242107120Sjulian	}
243107120Sjulian
244107120Sjulian	dcid = ch->dcid;
245107120Sjulian	con = ch->con;
246107120Sjulian
247107120Sjulian	/*
248107120Sjulian	 * Now we are pretty much sure it is our response. So create and send
249107120Sjulian	 * L2CAP_ConnectRsp message to our peer.
250107120Sjulian	 */
251107120Sjulian
252107120Sjulian	if (ch->ident != ip->ident)
253107120Sjulian		NG_L2CAP_WARN(
254107120Sjulian"%s: %s - channel ident and response ident do not match, scid=%d, ident=%d. " \
255107120Sjulian"Will use response ident=%d\n",
256107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->scid,
257107120Sjulian			ch->ident, ip->ident);
258107120Sjulian
259107120Sjulian	/* Check result */
260107120Sjulian	switch (ip->result) {
261107120Sjulian	case NG_L2CAP_SUCCESS:
262107120Sjulian		ch->state = NG_L2CAP_CONFIG;
263107120Sjulian		ch->cfg_state = 0;
264107120Sjulian		break;
265107120Sjulian
266107120Sjulian	case NG_L2CAP_PENDING:
267107120Sjulian		break;
268107120Sjulian
269107120Sjulian	default:
270107120Sjulian		ng_l2cap_free_chan(ch);
271107120Sjulian		ch = NULL;
272107120Sjulian		break;
273107120Sjulian	}
274107120Sjulian
275107120Sjulian	/* Create L2CAP command */
276107120Sjulian	cmd = ng_l2cap_new_cmd(con, ch, ip->ident, NG_L2CAP_CON_RSP,
277107120Sjulian			msg->header.token);
278107120Sjulian	if (cmd == NULL) {
279107120Sjulian		if (ch != NULL)
280107120Sjulian			ng_l2cap_free_chan(ch);
281107120Sjulian
282107120Sjulian		error = ENOMEM;
283107120Sjulian		goto out;
284107120Sjulian	}
285107120Sjulian
286107120Sjulian	_ng_l2cap_con_rsp(cmd->aux, cmd->ident, ip->lcid, dcid,
287107120Sjulian		ip->result, ip->status);
288107120Sjulian	if (cmd->aux == NULL) {
289107120Sjulian		if (ch != NULL)
290107120Sjulian			ng_l2cap_free_chan(ch);
291107120Sjulian
292107120Sjulian		ng_l2cap_free_cmd(cmd);
293107120Sjulian		error = ENOBUFS;
294107120Sjulian		goto out;
295107120Sjulian	}
296107120Sjulian
297107120Sjulian	/* Link command to the queue */
298107120Sjulian	ng_l2cap_link_cmd(con, cmd);
299107120Sjulian	ng_l2cap_lp_deliver(con);
300107120Sjulianout:
301107120Sjulian	return (error);
302107120Sjulian} /* ng_l2cap_l2ca_con_rsp_req */
303107120Sjulian
304107120Sjulian/*
305107120Sjulian * Send L2CAP_ConnectRsp response to the upper layer
306107120Sjulian */
307107120Sjulian
308107120Sjulianint
309107120Sjulianng_l2cap_l2ca_con_rsp_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
310107120Sjulian{
311107120Sjulian	ng_l2cap_p			 l2cap = ch->con->l2cap;
312107120Sjulian	struct ng_mesg			*msg = NULL;
313107120Sjulian	ng_l2cap_l2ca_con_rsp_op	*op = NULL;
314107120Sjulian	int				 error = 0;
315107120Sjulian
316107120Sjulian	/* Check if upstream hook is connected and valid */
317107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
318107120Sjulian		NG_L2CAP_ERR(
319107120Sjulian"%s: %s - unable to send L2CA_ConnectRsp response message. " \
320107120Sjulian"Hook is not connected or valid, psm=%d\n",
321107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
322107120Sjulian
323107120Sjulian		return (ENOTCONN);
324107120Sjulian	}
325107120Sjulian
326107120Sjulian	/* Create and send L2CA_ConnectRsp response message */
327107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_RSP,
328107120Sjulian		sizeof(*op), M_NOWAIT);
329107120Sjulian	if (msg == NULL)
330107120Sjulian		error = ENOMEM;
331107120Sjulian	else {
332107120Sjulian		msg->header.token = token;
333107120Sjulian		msg->header.flags |= NGF_RESP;
334107120Sjulian
335107120Sjulian		op = (ng_l2cap_l2ca_con_rsp_op *)(msg->data);
336107120Sjulian		op->result = result;
337107120Sjulian
338128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
339107120Sjulian	}
340107120Sjulian
341107120Sjulian	return (error);
342107120Sjulian} /* ng_l2cap_l2ca_con_rsp_rsp */
343107120Sjulian
344107120Sjulian/*
345107120Sjulian * Send L2CA_ConnectInd message to the upper layer protocol.
346107120Sjulian */
347107120Sjulian
348107120Sjulianint
349107120Sjulianng_l2cap_l2ca_con_ind(ng_l2cap_chan_p ch)
350107120Sjulian{
351107120Sjulian	ng_l2cap_p			 l2cap = ch->con->l2cap;
352107120Sjulian	struct ng_mesg			*msg = NULL;
353107120Sjulian	ng_l2cap_l2ca_con_ind_ip	*ip = NULL;
354107120Sjulian	int				 error = 0;
355107120Sjulian
356107120Sjulian	/* Check if upstream hook is connected and valid */
357107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
358107120Sjulian		NG_L2CAP_ERR(
359107120Sjulian"%s: %s - unable to send L2CA_ConnectInd message. " \
360107120Sjulian"Hook is not connected or valid, psm=%d\n",
361107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
362107120Sjulian
363107120Sjulian		return (ENOTCONN);
364107120Sjulian	}
365107120Sjulian
366107120Sjulian	/* Create and send L2CA_ConnectInd message */
367107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_IND,
368107120Sjulian		sizeof(*ip), M_NOWAIT);
369107120Sjulian	if (msg == NULL)
370107120Sjulian		error = ENOMEM;
371107120Sjulian	else {
372107120Sjulian		ip = (ng_l2cap_l2ca_con_ind_ip *)(msg->data);
373107120Sjulian
374107120Sjulian		bcopy(&ch->con->remote, &ip->bdaddr, sizeof(ip->bdaddr));
375107120Sjulian		ip->lcid = ch->scid;
376107120Sjulian		ip->psm = ch->psm;
377107120Sjulian		ip->ident = ch->ident;
378107120Sjulian
379128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
380107120Sjulian	}
381107120Sjulian
382107120Sjulian	return (error);
383107120Sjulian} /* ng_l2cap_l2ca_con_ind */
384107120Sjulian
385107120Sjulian/*
386107120Sjulian * Process L2CA_Config request from the upper layer protocol
387107120Sjulian */
388107120Sjulian
389107120Sjulianint
390107120Sjulianng_l2cap_l2ca_cfg_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
391107120Sjulian{
392107120Sjulian	ng_l2cap_l2ca_cfg_ip	*ip = NULL;
393107120Sjulian	ng_l2cap_chan_p		 ch = NULL;
394107120Sjulian	ng_l2cap_cmd_p		 cmd = NULL;
395107120Sjulian	struct mbuf		*opt = NULL;
396107120Sjulian        u_int16_t		*mtu = NULL, *flush_timo = NULL;
397107120Sjulian        ng_l2cap_flow_p		 flow = NULL;
398107120Sjulian	int			 error = 0;
399107120Sjulian
400107120Sjulian	/* Check message */
401107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
402107120Sjulian		NG_L2CAP_ALERT(
403107120Sjulian"%s: %s - Invalid L2CA_Config request message size, size=%d\n",
404107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
405107120Sjulian			msg->header.arglen);
406107120Sjulian		error = EMSGSIZE;
407107120Sjulian		goto out;
408107120Sjulian	}
409107120Sjulian
410107120Sjulian	ip = (ng_l2cap_l2ca_cfg_ip *)(msg->data);
411107120Sjulian
412107120Sjulian	/* Check if we have this channel */
413107120Sjulian	ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid);
414107120Sjulian	if (ch == NULL) {
415107120Sjulian		NG_L2CAP_ERR(
416107120Sjulian"%s: %s - unexpected L2CA_Config request message. " \
417107120Sjulian"Channel does not exist, lcid=%d\n",
418107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
419107120Sjulian		error = ENOENT;
420107120Sjulian		goto out;
421107120Sjulian	}
422107120Sjulian
423107120Sjulian	/* Check channel state */
424107120Sjulian	if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG) {
425107120Sjulian		NG_L2CAP_ERR(
426107120Sjulian"%s: %s - unexpected L2CA_Config request message. " \
427107120Sjulian"Invalid channel state, state=%d, lcid=%d\n",
428107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->state,
429107120Sjulian			ch->scid);
430107120Sjulian		error = EINVAL;
431107120Sjulian		goto out;
432107120Sjulian	}
433107120Sjulian
434107120Sjulian	/* Set requested channel configuration options */
435107120Sjulian	ch->imtu = ip->imtu;
436107120Sjulian	bcopy(&ip->oflow, &ch->oflow, sizeof(ch->oflow));
437107120Sjulian	ch->flush_timo = ip->flush_timo;
438107120Sjulian	ch->link_timo = ip->link_timo;
439107120Sjulian
440107120Sjulian	/* Compare channel settings with defaults */
441107120Sjulian	if (ch->imtu != NG_L2CAP_MTU_DEFAULT)
442107120Sjulian		mtu = &ch->imtu;
443107120Sjulian	if (ch->flush_timo != NG_L2CAP_FLUSH_TIMO_DEFAULT)
444107120Sjulian		flush_timo = &ch->flush_timo;
445107120Sjulian	if (bcmp(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow)) != 0)
446107120Sjulian		flow = &ch->oflow;
447107120Sjulian
448107120Sjulian	/* Create configuration options */
449107120Sjulian	_ng_l2cap_build_cfg_options(opt, mtu, flush_timo, flow);
450107120Sjulian	if (opt == NULL) {
451107120Sjulian                error = ENOBUFS;
452107120Sjulian		goto out;
453107120Sjulian	}
454107120Sjulian
455107120Sjulian	/* Create L2CAP command descriptor */
456107120Sjulian	cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(ch->con),
457107120Sjulian			NG_L2CAP_CFG_REQ, msg->header.token);
458107120Sjulian	if (cmd == NULL) {
459107120Sjulian		NG_FREE_M(opt);
460107120Sjulian		error = ENOMEM;
461107120Sjulian		goto out;
462107120Sjulian	}
463107120Sjulian
464107120Sjulian	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
465107120Sjulian		ng_l2cap_free_cmd(cmd);
466107120Sjulian		NG_FREE_M(opt);
467107120Sjulian		error = EIO;
468107120Sjulian		goto out;
469107120Sjulian	}
470107120Sjulian
471107120Sjulian	/* Create L2CAP command packet */
472107120Sjulian	_ng_l2cap_cfg_req(cmd->aux, cmd->ident, ch->dcid, 0, opt);
473107120Sjulian	if (cmd->aux == NULL) {
474107120Sjulian		ng_l2cap_free_cmd(cmd);
475107120Sjulian		error =  ENOBUFS;
476107120Sjulian		goto out;
477107120Sjulian	}
478107120Sjulian
479107120Sjulian	/* Adjust channel state for re-configuration */
480107120Sjulian	if (ch->state == NG_L2CAP_OPEN) {
481107120Sjulian		ch->state = NG_L2CAP_CONFIG;
482107120Sjulian		ch->cfg_state = 0;
483107120Sjulian	}
484107120Sjulian
485107120Sjulian        /* Link command to the queue */
486107120Sjulian	ng_l2cap_link_cmd(ch->con, cmd);
487107120Sjulian	ng_l2cap_lp_deliver(ch->con);
488107120Sjulianout:
489107120Sjulian	return (error);
490107120Sjulian} /* ng_l2cap_l2ca_cfg_req */
491107120Sjulian
492107120Sjulian/*
493107120Sjulian * Send L2CA_Config response to the upper layer protocol
494107120Sjulian */
495107120Sjulian
496107120Sjulianint
497107120Sjulianng_l2cap_l2ca_cfg_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
498107120Sjulian{
499107120Sjulian	ng_l2cap_p		 l2cap = ch->con->l2cap;
500107120Sjulian	struct ng_mesg		*msg = NULL;
501107120Sjulian	ng_l2cap_l2ca_cfg_op	*op = NULL;
502107120Sjulian	int			 error = 0;
503107120Sjulian
504107120Sjulian	/* Check if upstream hook is connected and valid */
505107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
506107120Sjulian		NG_L2CAP_ERR(
507107120Sjulian"%s: %s - unable to send L2CA_Config response message. " \
508107120Sjulian"Hook is not connected or valid, psm=%d\n",
509107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
510107120Sjulian
511107120Sjulian		return (ENOTCONN);
512107120Sjulian	}
513107120Sjulian
514107120Sjulian	/* Create and send L2CA_Config response message */
515107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG,
516107120Sjulian		sizeof(*op), M_NOWAIT);
517107120Sjulian	if (msg == NULL)
518107120Sjulian		error = ENOMEM;
519107120Sjulian	else {
520107120Sjulian		msg->header.token = token;
521107120Sjulian		msg->header.flags |= NGF_RESP;
522107120Sjulian
523107120Sjulian		op = (ng_l2cap_l2ca_cfg_op *)(msg->data);
524107120Sjulian		op->result = result;
525107120Sjulian		op->imtu = ch->imtu;
526107120Sjulian		bcopy(&ch->oflow, &op->oflow, sizeof(op->oflow));
527107120Sjulian		op->flush_timo = ch->flush_timo;
528107120Sjulian
529128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
530107120Sjulian
531107120Sjulian		if (error == 0 && result == NG_L2CAP_SUCCESS) {
532107120Sjulian			ch->cfg_state |= NG_L2CAP_CFG_IN;
533107120Sjulian
534107120Sjulian			if (ch->cfg_state == NG_L2CAP_CFG_BOTH)
535107120Sjulian				ch->state = NG_L2CAP_OPEN;
536107120Sjulian		}
537107120Sjulian	}
538107120Sjulian
539107120Sjulian	return (error);
540107120Sjulian} /* ng_l2cap_l2ca_cfg_rsp */
541107120Sjulian
542107120Sjulian/*
543107120Sjulian * Process L2CA_ConfigRsp request from the upper layer protocol
544107120Sjulian *
545107120Sjulian * XXX XXX XXX
546107120Sjulian *
547107120Sjulian * NOTE: The Bluetooth specification says that Configuration_Response
548107120Sjulian * (L2CA_ConfigRsp) should be used to issue response to configuration request
549107120Sjulian * indication. The minor problem here is L2CAP command ident. We should use
550107120Sjulian * ident from original L2CAP request to make sure our peer can match request
551107120Sjulian * and response. For some reason Bluetooth specification does not include
552107120Sjulian * ident field into L2CA_ConfigInd and L2CA_ConfigRsp messages. This seems
553107120Sjulian * strange to me, because L2CA_ConnectInd and L2CA_ConnectRsp do have ident
554107120Sjulian * field. So we should store last known L2CAP request command ident in channel.
555107120Sjulian * Also it seems that upper layer can not reject configuration request, as
556107120Sjulian * Configuration_Response message does not have status/reason field.
557107120Sjulian */
558107120Sjulian
559107120Sjulianint
560107120Sjulianng_l2cap_l2ca_cfg_rsp_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
561107120Sjulian{
562107120Sjulian	ng_l2cap_l2ca_cfg_rsp_ip	*ip = NULL;
563107120Sjulian	ng_l2cap_chan_p			 ch = NULL;
564107120Sjulian	ng_l2cap_cmd_p			 cmd = NULL;
565107120Sjulian	struct mbuf			*opt = NULL;
566107120Sjulian	u_int16_t			*mtu = NULL;
567107120Sjulian	ng_l2cap_flow_p			 flow = NULL;
568107120Sjulian	int				 error = 0;
569107120Sjulian
570107120Sjulian	/* Check message */
571107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
572107120Sjulian		NG_L2CAP_ALERT(
573107120Sjulian"%s: %s - invalid L2CA_ConfigRsp request message size, size=%d\n",
574107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
575107120Sjulian			msg->header.arglen);
576107120Sjulian		error = EMSGSIZE;
577107120Sjulian		goto out;
578107120Sjulian	}
579107120Sjulian
580107120Sjulian	ip = (ng_l2cap_l2ca_cfg_rsp_ip *)(msg->data);
581107120Sjulian
582107120Sjulian	/* Check if we have this channel */
583107120Sjulian	ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid);
584107120Sjulian	if (ch == NULL) {
585107120Sjulian		NG_L2CAP_ERR(
586107120Sjulian"%s: %s - unexpected L2CA_ConfigRsp request message. " \
587107120Sjulian"Channel does not exist, lcid=%d\n",
588107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
589107120Sjulian		error = ENOENT;
590107120Sjulian		goto out;
591107120Sjulian	}
592107120Sjulian
593107120Sjulian	/* Check channel state */
594107120Sjulian	if (ch->state != NG_L2CAP_CONFIG) {
595107120Sjulian		NG_L2CAP_ERR(
596107120Sjulian"%s: %s - unexpected L2CA_ConfigRsp request message. " \
597107120Sjulian"Invalid channel state, state=%d, lcid=%d\n",
598107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->state,
599107120Sjulian			ch->scid);
600107120Sjulian		error = EINVAL;
601107120Sjulian		goto out;
602107120Sjulian	}
603107120Sjulian
604107120Sjulian	/* Set channel settings */
605107120Sjulian	if (ip->omtu != ch->omtu) {
606107120Sjulian		ch->omtu = ip->omtu;
607107120Sjulian		mtu = &ch->omtu;
608107120Sjulian	}
609107120Sjulian
610107120Sjulian	if (bcmp(&ip->iflow, &ch->iflow, sizeof(ch->iflow)) != 0) {
611107120Sjulian		bcopy(&ip->iflow, &ch->iflow, sizeof(ch->iflow));
612107120Sjulian		flow = &ch->iflow;
613107120Sjulian	}
614107120Sjulian
615107120Sjulian	if (mtu != NULL || flow != NULL) {
616107120Sjulian		_ng_l2cap_build_cfg_options(opt, mtu, NULL, flow);
617107120Sjulian		if (opt == NULL) {
618107120Sjulian			error = ENOBUFS;
619107120Sjulian			goto out;
620107120Sjulian		}
621107120Sjulian	}
622107120Sjulian
623107120Sjulian	/* Create L2CAP command */
624107120Sjulian	cmd = ng_l2cap_new_cmd(ch->con, ch, ch->ident, NG_L2CAP_CFG_RSP,
625107120Sjulian			msg->header.token);
626107120Sjulian	if (cmd == NULL) {
627107120Sjulian		NG_FREE_M(opt);
628107120Sjulian		error = ENOMEM;
629107120Sjulian		goto out;
630107120Sjulian	}
631107120Sjulian
632107120Sjulian	_ng_l2cap_cfg_rsp(cmd->aux,cmd->ident,ch->dcid,0,NG_L2CAP_SUCCESS,opt);
633107120Sjulian	if (cmd->aux == NULL) {
634107120Sjulian		ng_l2cap_free_cmd(cmd);
635107120Sjulian		error = ENOBUFS;
636107120Sjulian		goto out;
637107120Sjulian	}
638107120Sjulian
639107120Sjulian	/* XXX FIXME - not here ??? */
640107120Sjulian	ch->cfg_state |= NG_L2CAP_CFG_OUT;
641107120Sjulian	if (ch->cfg_state == NG_L2CAP_CFG_BOTH)
642107120Sjulian		ch->state = NG_L2CAP_OPEN;
643107120Sjulian
644107120Sjulian	/* Link command to the queue */
645107120Sjulian	ng_l2cap_link_cmd(ch->con, cmd);
646107120Sjulian	ng_l2cap_lp_deliver(ch->con);
647107120Sjulianout:
648107120Sjulian	return (error);
649107120Sjulian} /* ng_l2cap_l2ca_cfg_rsp_req */
650107120Sjulian
651107120Sjulian/*
652107120Sjulian * Send L2CA_ConfigRsp response to the upper layer protocol
653107120Sjulian */
654107120Sjulian
655107120Sjulianint
656107120Sjulianng_l2cap_l2ca_cfg_rsp_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
657107120Sjulian{
658107120Sjulian	ng_l2cap_p			 l2cap = ch->con->l2cap;
659107120Sjulian	struct ng_mesg			*msg = NULL;
660107120Sjulian	ng_l2cap_l2ca_cfg_rsp_op	*op = NULL;
661107120Sjulian	int				 error = 0;
662107120Sjulian
663107120Sjulian	/* Check if upstream hook is connected and valid */
664107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
665107120Sjulian		NG_L2CAP_ERR(
666107120Sjulian"%s: %s - unable to send L2CA_ConfigRsp response message. " \
667107120Sjulian"Hook is not connected or valid, psm=%d\n",
668107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
669107120Sjulian
670107120Sjulian		return (ENOTCONN);
671107120Sjulian	}
672107120Sjulian
673107120Sjulian	/* Create and send L2CA_ConfigRsp response message */
674107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_RSP,
675107120Sjulian		sizeof(*op), M_NOWAIT);
676107120Sjulian	if (msg == NULL)
677107120Sjulian		error = ENOMEM;
678107120Sjulian	else {
679107120Sjulian		msg->header.token = token;
680107120Sjulian		msg->header.flags |= NGF_RESP;
681107120Sjulian
682107120Sjulian		op = (ng_l2cap_l2ca_cfg_rsp_op *)(msg->data);
683107120Sjulian		op->result = result;
684107120Sjulian
685128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
686107120Sjulian	}
687107120Sjulian
688107120Sjulian	return (error);
689107120Sjulian} /* ng_l2cap_l2ca_cfg_rsp_rsp */
690107120Sjulian
691107120Sjulian/*
692107120Sjulian * Send L2CA_ConfigInd message to the upper layer protocol
693107120Sjulian *
694107120Sjulian * XXX XXX XXX
695107120Sjulian *
696107120Sjulian * NOTE: The Bluetooth specification says that Configuration_Response
697107120Sjulian * (L2CA_ConfigRsp) should be used to issue response to configuration request
698107120Sjulian * indication. The minor problem here is L2CAP command ident. We should use
699107120Sjulian * ident from original L2CAP request to make sure our peer can match request
700107120Sjulian * and response. For some reason Bluetooth specification does not include
701107120Sjulian * ident field into L2CA_ConfigInd and L2CA_ConfigRsp messages. This seems
702107120Sjulian * strange to me, because L2CA_ConnectInd and L2CA_ConnectRsp do have ident
703107120Sjulian * field. So we should store last known L2CAP request command ident in channel.
704107120Sjulian * Also it seems that upper layer can not reject configuration request, as
705107120Sjulian * Configuration_Response message does not have status/reason field.
706107120Sjulian */
707107120Sjulian
708107120Sjulianint
709107120Sjulianng_l2cap_l2ca_cfg_ind(ng_l2cap_chan_p ch)
710107120Sjulian{
711107120Sjulian	ng_l2cap_p			 l2cap = ch->con->l2cap;
712107120Sjulian	struct ng_mesg			*msg = NULL;
713107120Sjulian	ng_l2cap_l2ca_cfg_ind_ip	*ip = NULL;
714107120Sjulian	int				 error = 0;
715107120Sjulian
716107120Sjulian	/* Check if upstream hook is connected and valid */
717107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
718107120Sjulian		NG_L2CAP_ERR(
719107120Sjulian"%s: %s - Unable to send L2CA_ConfigInd message. " \
720107120Sjulian"Hook is not connected or valid, psm=%d\n",
721107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
722107120Sjulian
723107120Sjulian		return (ENOTCONN);
724107120Sjulian	}
725107120Sjulian
726107120Sjulian	/* Create and send L2CA_ConnectInd message */
727107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_IND,
728107120Sjulian			sizeof(*ip), M_NOWAIT);
729107120Sjulian	if (msg == NULL)
730107120Sjulian		error = ENOMEM;
731107120Sjulian	else {
732107120Sjulian		ip = (ng_l2cap_l2ca_cfg_ind_ip *)(msg->data);
733107120Sjulian		ip->lcid = ch->scid;
734107120Sjulian		ip->omtu = ch->omtu;
735107120Sjulian		bcopy(&ch->iflow, &ip->iflow, sizeof(ip->iflow));
736107120Sjulian		ip->flush_timo = ch->flush_timo;
737107120Sjulian
738128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
739107120Sjulian	}
740107120Sjulian
741107120Sjulian	return (error);
742107120Sjulian} /* ng_l2cap_l2ca_cfg_ind */
743107120Sjulian
744107120Sjulian/*
745107120Sjulian * Process L2CA_Write event
746107120Sjulian */
747107120Sjulian
748107120Sjulianint
749107120Sjulianng_l2cap_l2ca_write_req(ng_l2cap_p l2cap, struct mbuf *m)
750107120Sjulian{
751107120Sjulian	ng_l2cap_l2ca_hdr_t	*l2ca_hdr = NULL;
752107120Sjulian	ng_l2cap_chan_p		 ch = NULL;
753107120Sjulian	ng_l2cap_cmd_p		 cmd = NULL;
754107120Sjulian	int			 error = 0;
755107120Sjulian	u_int32_t		 token = 0;
756107120Sjulian
757107120Sjulian	/* Make sure we can access L2CA data packet header */
758107120Sjulian	if (m->m_pkthdr.len < sizeof(*l2ca_hdr)) {
759107120Sjulian		NG_L2CAP_ERR(
760107120Sjulian"%s: %s - L2CA Data packet too small, len=%d\n",
761107120Sjulian			__func__,NG_NODE_NAME(l2cap->node),m->m_pkthdr.len);
762107120Sjulian		error = EMSGSIZE;
763107120Sjulian		goto drop;
764107120Sjulian	}
765107120Sjulian
766107120Sjulian	/* Get L2CA data packet header */
767107120Sjulian	NG_L2CAP_M_PULLUP(m, sizeof(*l2ca_hdr));
768107120Sjulian	if (m == NULL)
769107120Sjulian		return (ENOBUFS);
770107120Sjulian
771107120Sjulian	l2ca_hdr = mtod(m, ng_l2cap_l2ca_hdr_t *);
772107120Sjulian	token = l2ca_hdr->token;
773107120Sjulian	m_adj(m, sizeof(*l2ca_hdr));
774107120Sjulian
775107120Sjulian	/* Verify payload size */
776107120Sjulian	if (l2ca_hdr->length != m->m_pkthdr.len) {
777107120Sjulian		NG_L2CAP_ERR(
778107120Sjulian"%s: %s - invalid L2CA Data packet. " \
779107120Sjulian"Payload length does not match, length=%d, len=%d\n",
780107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->length,
781107120Sjulian			m->m_pkthdr.len);
782107120Sjulian		error = EMSGSIZE;
783107120Sjulian		goto drop;
784107120Sjulian	}
785107120Sjulian
786107120Sjulian	/* Check channel ID */
787107120Sjulian	if (l2ca_hdr->lcid < NG_L2CAP_FIRST_CID) {
788107120Sjulian		NG_L2CAP_ERR(
789107120Sjulian"%s: %s - invalid L2CA Data packet. Inavlid channel ID, cid=%d\n",
790107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->lcid);
791107120Sjulian		error = EINVAL;
792107120Sjulian		goto drop;
793107120Sjulian	}
794107120Sjulian
795107120Sjulian	/* Verify that we have the channel and make sure it is open */
796107120Sjulian	ch = ng_l2cap_chan_by_scid(l2cap, l2ca_hdr->lcid);
797107120Sjulian	if (ch == NULL) {
798107120Sjulian		NG_L2CAP_ERR(
799107120Sjulian"%s: %s - invalid L2CA Data packet. Channel does not exist, cid=%d\n",
800107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->lcid);
801107120Sjulian		error = ENOENT;
802107120Sjulian		goto drop;
803107120Sjulian	}
804107120Sjulian
805107120Sjulian	if (ch->state != NG_L2CAP_OPEN) {
806107120Sjulian		NG_L2CAP_ERR(
807107120Sjulian"%s: %s - invalid L2CA Data packet. Invalid channel state, scid=%d, state=%d\n",
808107120Sjulian			 __func__, NG_NODE_NAME(l2cap->node), ch->scid,
809107120Sjulian			ch->state);
810107120Sjulian		error = EHOSTDOWN;
811107120Sjulian		goto drop; /* XXX not always - re-configure */
812107120Sjulian	}
813107120Sjulian
814107120Sjulian	/* Create L2CAP command descriptor */
815107120Sjulian	cmd = ng_l2cap_new_cmd(ch->con, ch, 0, NGM_L2CAP_L2CA_WRITE, token);
816107120Sjulian	if (cmd == NULL) {
817107120Sjulian		error = ENOMEM;
818107120Sjulian		goto drop;
819107120Sjulian	}
820107120Sjulian
821107120Sjulian	/* Attach data packet and link command to the queue */
822107120Sjulian	cmd->aux = m;
823107120Sjulian	ng_l2cap_link_cmd(ch->con, cmd);
824107120Sjulian	ng_l2cap_lp_deliver(ch->con);
825107120Sjulian
826107120Sjulian	return (error);
827107120Sjuliandrop:
828107120Sjulian	NG_FREE_M(m);
829107120Sjulian
830107120Sjulian	return (error);
831107120Sjulian} /* ng_l2cap_l2ca_write_req */
832107120Sjulian
833107120Sjulian/*
834107120Sjulian * Send L2CA_Write response
835107120Sjulian */
836107120Sjulian
837107120Sjulianint
838107120Sjulianng_l2cap_l2ca_write_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result,
839107120Sjulian		u_int16_t length)
840107120Sjulian{
841107120Sjulian	ng_l2cap_p		 l2cap = ch->con->l2cap;
842107120Sjulian	struct ng_mesg		*msg = NULL;
843107120Sjulian	ng_l2cap_l2ca_write_op	*op = NULL;
844107120Sjulian	int			 error = 0;
845107120Sjulian
846107120Sjulian	/* Check if upstream hook is connected and valid */
847107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
848107120Sjulian		NG_L2CAP_ERR(
849107120Sjulian"%s: %s - unable to send L2CA_WriteRsp message. " \
850107120Sjulian"Hook is not connected or valid, psm=%d\n",
851107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
852107120Sjulian
853107120Sjulian		return (ENOTCONN);
854107120Sjulian	}
855107120Sjulian
856107120Sjulian	/* Create and send L2CA_WriteRsp message */
857107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_WRITE,
858107120Sjulian			sizeof(*op), M_NOWAIT);
859107120Sjulian	if (msg == NULL)
860107120Sjulian		error = ENOMEM;
861107120Sjulian	else {
862107120Sjulian		msg->header.token = token;
863107120Sjulian		msg->header.flags |= NGF_RESP;
864107120Sjulian
865107120Sjulian		op = (ng_l2cap_l2ca_write_op *)(msg->data);
866107120Sjulian		op->result = result;
867107120Sjulian		op->length = length;
868107120Sjulian		op->lcid   = ch->scid;
869107120Sjulian
870128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
871107120Sjulian	}
872107120Sjulian
873107120Sjulian	return (error);
874107120Sjulian} /* ng_l2cap_l2ca_write_rsp */
875107120Sjulian
876107120Sjulian/*
877107120Sjulian * Receive packet from the lower layer protocol and send it to the upper
878107120Sjulian * layer protocol (L2CAP_Read)
879107120Sjulian */
880107120Sjulian
881107120Sjulianint
882107120Sjulianng_l2cap_l2ca_receive(ng_l2cap_con_p con)
883107120Sjulian{
884107120Sjulian	ng_l2cap_p	 l2cap = con->l2cap;
885107120Sjulian	ng_l2cap_hdr_t	*hdr = NULL;
886107120Sjulian	ng_l2cap_chan_p  ch = NULL;
887107120Sjulian	int		 error = 0;
888107120Sjulian
889107120Sjulian	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
890107120Sjulian	if (con->rx_pkt == NULL)
891107120Sjulian		return (ENOBUFS);
892107120Sjulian
893107120Sjulian	hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *);
894107120Sjulian
895107120Sjulian	/* Check channel */
896107120Sjulian	ch = ng_l2cap_chan_by_scid(l2cap, hdr->dcid);
897107120Sjulian	if (ch == NULL) {
898107120Sjulian		NG_L2CAP_ERR(
899107120Sjulian"%s: %s - unexpected L2CAP data packet. Channel does not exist, cid=%d\n",
900107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), hdr->dcid);
901107120Sjulian		error = ENOENT;
902107120Sjulian		goto drop;
903107120Sjulian	}
904107120Sjulian
905107120Sjulian	/* Check channel state */
906107120Sjulian	if (ch->state != NG_L2CAP_OPEN) {
907107120Sjulian		NG_L2CAP_WARN(
908107120Sjulian"%s: %s - unexpected L2CAP data packet. " \
909107120Sjulian"Invalid channel state, cid=%d, state=%d\n",
910107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->scid,
911107120Sjulian			ch->state);
912107120Sjulian		error = EHOSTDOWN; /* XXX not always - re-configuration */
913107120Sjulian		goto drop;
914107120Sjulian	}
915107120Sjulian
916107120Sjulian	/* Check payload size and channel's MTU */
917107120Sjulian	if (hdr->length > ch->imtu) {
918107120Sjulian		NG_L2CAP_ERR(
919107120Sjulian"%s: %s - invalid L2CAP data packet. " \
920107120Sjulian"Packet too big, length=%d, imtu=%d, cid=%d\n",
921107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), hdr->length,
922107120Sjulian			ch->imtu, ch->scid);
923107120Sjulian		error = EMSGSIZE;
924107120Sjulian		goto drop;
925107120Sjulian	}
926107120Sjulian
927107120Sjulian	/*
928107120Sjulian	 * If we got here then everything looks good and we can sent packet
929107120Sjulian	 * to the upper layer protocol.
930107120Sjulian	 */
931107120Sjulian
932107120Sjulian	/* Check if upstream hook is connected and valid */
933107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
934107120Sjulian		NG_L2CAP_ERR(
935107120Sjulian"%s: %s - unable to send L2CAP data packet. " \
936107120Sjulian"Hook is not connected or valid, psm=%d\n",
937107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
938107120Sjulian		error = ENOTCONN;
939107120Sjulian		goto drop;
940107120Sjulian	}
941107120Sjulian
942107120Sjulian	NG_SEND_DATA_ONLY(error, l2cap->l2c, con->rx_pkt);
943107120Sjulian	con->rx_pkt = NULL;
944107120Sjuliandrop:
945107120Sjulian	NG_FREE_M(con->rx_pkt); /* checks for != NULL */
946107120Sjulian
947107120Sjulian	return (error);
948107120Sjulian} /* ng_l2cap_receive */
949107120Sjulian
950107120Sjulian/*
951107120Sjulian * Receive connectioless (multicast) packet from the lower layer protocol and
952107120Sjulian * send it to the upper layer protocol
953107120Sjulian */
954107120Sjulian
955107120Sjulianint
956107120Sjulianng_l2cap_l2ca_clt_receive(ng_l2cap_con_p con)
957107120Sjulian{
958107120Sjulian	struct _clt_pkt {
959107120Sjulian		ng_l2cap_hdr_t		 h;
960107120Sjulian		ng_l2cap_clt_hdr_t	 c_h;
961107120Sjulian	} __attribute__ ((packed))	*hdr = NULL;
962107120Sjulian	ng_l2cap_p			 l2cap = con->l2cap;
963107120Sjulian	int				 length, error = 0;
964107120Sjulian
965107120Sjulian	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
966107120Sjulian	if (con->rx_pkt == NULL)
967107120Sjulian		return (ENOBUFS);
968107120Sjulian
969107120Sjulian	hdr = mtod(con->rx_pkt, struct _clt_pkt *);
970107120Sjulian
971107120Sjulian	/* Check packet */
972107120Sjulian	length = con->rx_pkt->m_pkthdr.len - sizeof(*hdr);
973107120Sjulian	if (length < 0) {
974107120Sjulian		NG_L2CAP_ERR(
975107120Sjulian"%s: %s - invalid L2CAP CLT data packet. Packet too small, length=%d\n",
976107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), length);
977107120Sjulian		error = EMSGSIZE;
978107120Sjulian		goto drop;
979107120Sjulian	}
980107120Sjulian
981107120Sjulian	/* Check payload size against CLT MTU */
982107120Sjulian	if (length > NG_L2CAP_MTU_DEFAULT) {
983107120Sjulian		NG_L2CAP_ERR(
984107120Sjulian"%s: %s - invalid L2CAP CLT data packet. Packet too big, length=%d, mtu=%d\n",
985107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), length,
986107120Sjulian			NG_L2CAP_MTU_DEFAULT);
987107120Sjulian		error = EMSGSIZE;
988107120Sjulian		goto drop;
989107120Sjulian	}
990107120Sjulian
991107120Sjulian	hdr->c_h.psm = le16toh(hdr->c_h.psm);
992107120Sjulian
993107120Sjulian	/*
994107120Sjulian	 * If we got here then everything looks good and we can sent packet
995107120Sjulian	 * to the upper layer protocol.
996107120Sjulian	 */
997107120Sjulian
998107120Sjulian	/* Select upstream hook based on PSM */
999107120Sjulian	switch (hdr->c_h.psm) {
1000107120Sjulian	case NG_L2CAP_PSM_SDP:
1001107120Sjulian		if (l2cap->flags & NG_L2CAP_CLT_SDP_DISABLED)
1002107120Sjulian			goto drop;
1003107120Sjulian		break;
1004107120Sjulian
1005107120Sjulian	case NG_L2CAP_PSM_RFCOMM:
1006107120Sjulian		if (l2cap->flags & NG_L2CAP_CLT_RFCOMM_DISABLED)
1007107120Sjulian			goto drop;
1008107120Sjulian		break;
1009107120Sjulian
1010107120Sjulian	case NG_L2CAP_PSM_TCP:
1011107120Sjulian		if (l2cap->flags & NG_L2CAP_CLT_TCP_DISABLED)
1012107120Sjulian			goto drop;
1013107120Sjulian		break;
1014107120Sjulian        }
1015107120Sjulian
1016107120Sjulian	/* Check if upstream hook is connected and valid */
1017107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1018107120Sjulian		NG_L2CAP_ERR(
1019107120Sjulian"%s: %s - unable to send L2CAP CLT data packet. " \
1020107120Sjulian"Hook is not connected or valid, psm=%d\n",
1021107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), hdr->c_h.psm);
1022107120Sjulian		error = ENOTCONN;
1023107120Sjulian		goto drop;
1024107120Sjulian	}
1025107120Sjulian
1026107120Sjulian	NG_SEND_DATA_ONLY(error, l2cap->l2c, con->rx_pkt);
1027107120Sjulian	con->rx_pkt = NULL;
1028107120Sjuliandrop:
1029107120Sjulian	NG_FREE_M(con->rx_pkt); /* checks for != NULL */
1030107120Sjulian
1031107120Sjulian	return (error);
1032107120Sjulian} /* ng_l2cap_l2ca_clt_receive */
1033107120Sjulian
1034107120Sjulian/*
1035107120Sjulian * Send L2CA_QoSViolationInd to the upper layer protocol
1036107120Sjulian */
1037107120Sjulian
1038107120Sjulianint
1039107120Sjulianng_l2cap_l2ca_qos_ind(ng_l2cap_chan_p ch)
1040107120Sjulian{
1041107120Sjulian	ng_l2cap_p			 l2cap = ch->con->l2cap;
1042107120Sjulian	struct ng_mesg			*msg = NULL;
1043107120Sjulian	ng_l2cap_l2ca_qos_ind_ip	*ip = NULL;
1044107120Sjulian	int				 error = 0;
1045107120Sjulian
1046107120Sjulian	/* Check if upstream hook is connected and valid */
1047107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1048107120Sjulian		NG_L2CAP_ERR(
1049107120Sjulian"%s: %s - unable to send L2CA_QoSViolationInd message. " \
1050107120Sjulian"Hook is not connected or valid, psm=%d\n",
1051107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
1052107120Sjulian
1053107120Sjulian		return (ENOTCONN);
1054107120Sjulian	}
1055107120Sjulian
1056107120Sjulian	/* Create and send L2CA_QoSViolationInd message */
1057107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_QOS_IND,
1058107120Sjulian		sizeof(*ip), M_NOWAIT);
1059107120Sjulian	if (msg == NULL)
1060107120Sjulian		error = ENOMEM;
1061107120Sjulian	else {
1062107120Sjulian		ip = (ng_l2cap_l2ca_qos_ind_ip *)(msg->data);
1063107120Sjulian		bcopy(&ch->con->remote, &ip->bdaddr, sizeof(ip->bdaddr));
1064128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
1065107120Sjulian	}
1066107120Sjulian
1067107120Sjulian	return (error);
1068107120Sjulian} /* ng_l2cap_l2ca_qos_ind */
1069107120Sjulian
1070107120Sjulian/*
1071107120Sjulian * Process L2CA_Disconnect request from the upper layer protocol.
1072107120Sjulian */
1073107120Sjulian
1074107120Sjulianint
1075107120Sjulianng_l2cap_l2ca_discon_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
1076107120Sjulian{
1077107120Sjulian	ng_l2cap_l2ca_discon_ip	*ip = NULL;
1078107120Sjulian	ng_l2cap_chan_p		 ch = NULL;
1079107120Sjulian	ng_l2cap_cmd_p		 cmd = NULL;
1080107120Sjulian	int			 error = 0;
1081107120Sjulian
1082107120Sjulian	/* Check message */
1083107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
1084107120Sjulian		NG_L2CAP_ALERT(
1085107120Sjulian"%s: %s - invalid L2CA_Disconnect request message size, size=%d\n",
1086107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
1087107120Sjulian			msg->header.arglen);
1088107120Sjulian		error = EMSGSIZE;
1089107120Sjulian		goto out;
1090107120Sjulian	}
1091107120Sjulian
1092107120Sjulian	ip = (ng_l2cap_l2ca_discon_ip *)(msg->data);
1093107120Sjulian
1094107120Sjulian	/* Check if we have this channel */
1095107120Sjulian	ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid);
1096107120Sjulian	if (ch == NULL) {
1097107120Sjulian		NG_L2CAP_ERR(
1098107120Sjulian"%s: %s - unexpected L2CA_Disconnect request message. " \
1099107120Sjulian"Channel does not exist, lcid=%d\n",
1100107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
1101107120Sjulian		error = ENOENT;
1102107120Sjulian		goto out;
1103107120Sjulian	}
1104107120Sjulian
1105107120Sjulian	/* Check channel state */
1106107120Sjulian	if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN &&
1107107120Sjulian	    ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
1108107120Sjulian		NG_L2CAP_ERR(
1109107120Sjulian"%s: %s - unexpected L2CA_Disconnect request message. " \
1110107120Sjulian"Invalid channel state, state=%d, lcid=%d\n",
1111107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->state,
1112107120Sjulian			ch->scid);
1113107120Sjulian		error = EINVAL;
1114107120Sjulian		goto out;
1115107120Sjulian	}
1116107120Sjulian
1117107120Sjulian	/* Create and send L2CAP_DisconReq message */
1118107120Sjulian	cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(ch->con),
1119107120Sjulian			NG_L2CAP_DISCON_REQ, msg->header.token);
1120107120Sjulian	if (cmd == NULL) {
1121107120Sjulian		ng_l2cap_free_chan(ch);
1122107120Sjulian		error = ENOMEM;
1123107120Sjulian		goto out;
1124107120Sjulian	}
1125107120Sjulian
1126107120Sjulian	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
1127107120Sjulian		ng_l2cap_free_chan(ch);
1128107120Sjulian		ng_l2cap_free_cmd(cmd);
1129107120Sjulian		error = EIO;
1130107120Sjulian		goto out;
1131107120Sjulian	}
1132107120Sjulian
1133107120Sjulian	_ng_l2cap_discon_req(cmd->aux, cmd->ident, ch->dcid, ch->scid);
1134107120Sjulian	if (cmd->aux == NULL) {
1135107120Sjulian		ng_l2cap_free_chan(ch);
1136107120Sjulian		ng_l2cap_free_cmd(cmd);
1137107120Sjulian		error = ENOBUFS;
1138107120Sjulian		goto out;
1139107120Sjulian	}
1140107120Sjulian
1141107120Sjulian	ch->state = NG_L2CAP_W4_L2CAP_DISCON_RSP;
1142107120Sjulian
1143107120Sjulian	/* Link command to the queue */
1144107120Sjulian	ng_l2cap_link_cmd(ch->con, cmd);
1145107120Sjulian	ng_l2cap_lp_deliver(ch->con);
1146107120Sjulianout:
1147107120Sjulian	return (error);
1148107120Sjulian} /* ng_l2cap_l2ca_discon_req */
1149107120Sjulian
1150107120Sjulian/*
1151107120Sjulian * Send L2CA_Disconnect response to the upper layer protocol
1152107120Sjulian */
1153107120Sjulian
1154107120Sjulianint
1155107120Sjulianng_l2cap_l2ca_discon_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
1156107120Sjulian{
1157107120Sjulian	ng_l2cap_p		 l2cap = ch->con->l2cap;
1158107120Sjulian	struct ng_mesg		*msg = NULL;
1159107120Sjulian	ng_l2cap_l2ca_discon_op	*op = NULL;
1160107120Sjulian	int			 error = 0;
1161107120Sjulian
1162107120Sjulian	/* Check if upstream hook is connected and valid */
1163107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1164107120Sjulian		NG_L2CAP_ERR(
1165107120Sjulian"%s: %s - unable to send L2CA_Disconnect response message. " \
1166107120Sjulian"Hook is not connected or valid, psm=%d\n",
1167107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
1168107120Sjulian
1169107120Sjulian		return (ENOTCONN);
1170107120Sjulian	}
1171107120Sjulian
1172107120Sjulian	/* Create and send L2CA_Disconnect response message */
1173107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON,
1174107120Sjulian		sizeof(*op), M_NOWAIT);
1175107120Sjulian	if (msg == NULL)
1176107120Sjulian		error = ENOMEM;
1177107120Sjulian	else {
1178107120Sjulian		msg->header.token = token;
1179107120Sjulian		msg->header.flags |= NGF_RESP;
1180107120Sjulian
1181107120Sjulian		op = (ng_l2cap_l2ca_discon_op *)(msg->data);
1182107120Sjulian		op->result = result;
1183107120Sjulian
1184128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
1185107120Sjulian	}
1186107120Sjulian
1187107120Sjulian	return (error);
1188107120Sjulian} /* ng_l2cap_l2ca_discon_rsp */
1189107120Sjulian
1190107120Sjulian/*
1191107120Sjulian * Send L2CA_DisconnectInd message to the upper layer protocol.
1192107120Sjulian */
1193107120Sjulian
1194107120Sjulianint
1195107120Sjulianng_l2cap_l2ca_discon_ind(ng_l2cap_chan_p ch)
1196107120Sjulian{
1197107120Sjulian	ng_l2cap_p			 l2cap = ch->con->l2cap;
1198107120Sjulian	struct ng_mesg			*msg = NULL;
1199107120Sjulian	ng_l2cap_l2ca_discon_ind_ip	*ip = NULL;
1200107120Sjulian	int				 error = 0;
1201107120Sjulian
1202107120Sjulian	/* Check if upstream hook is connected and valid */
1203107120Sjulian	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1204107120Sjulian		NG_L2CAP_ERR(
1205107120Sjulian"%s: %s - unable to send L2CA_DisconnectInd message. " \
1206107120Sjulian"Hook is not connected or valid, psm=%d\n",
1207107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
1208107120Sjulian
1209107120Sjulian		return (ENOTCONN);
1210107120Sjulian	}
1211107120Sjulian
1212107120Sjulian	/* Create and send L2CA_DisconnectInd message */
1213107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON_IND,
1214107120Sjulian		sizeof(*ip), M_NOWAIT);
1215107120Sjulian	if (msg == NULL)
1216107120Sjulian		error = ENOMEM;
1217107120Sjulian	else {
1218107120Sjulian		ip = (ng_l2cap_l2ca_discon_ind_ip *)(msg->data);
1219107120Sjulian		ip->lcid = ch->scid;
1220128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
1221107120Sjulian	}
1222107120Sjulian
1223107120Sjulian	return (error);
1224107120Sjulian} /* ng_l2cap_l2ca_discon_ind */
1225107120Sjulian
1226107120Sjulian/*
1227107120Sjulian * Process L2CA_GroupCreate request from the upper layer protocol.
1228107120Sjulian * XXX FIXME
1229107120Sjulian */
1230107120Sjulian
1231107120Sjulianint
1232107120Sjulianng_l2cap_l2ca_grp_create(ng_l2cap_p l2cap, struct ng_mesg *msg)
1233107120Sjulian{
1234107120Sjulian	return (ENOTSUP);
1235107120Sjulian} /* ng_l2cap_l2ca_grp_create */
1236107120Sjulian
1237107120Sjulian/*
1238107120Sjulian * Process L2CA_GroupClose request from the upper layer protocol
1239107120Sjulian * XXX FIXME
1240107120Sjulian */
1241107120Sjulian
1242107120Sjulianint
1243107120Sjulianng_l2cap_l2ca_grp_close(ng_l2cap_p l2cap, struct ng_mesg *msg)
1244107120Sjulian{
1245107120Sjulian	return (ENOTSUP);
1246107120Sjulian} /* ng_l2cap_l2ca_grp_close */
1247107120Sjulian
1248107120Sjulian/*
1249107120Sjulian * Process L2CA_GroupAddMember request from the upper layer protocol.
1250107120Sjulian * XXX FIXME
1251107120Sjulian */
1252107120Sjulian
1253107120Sjulianint
1254107120Sjulianng_l2cap_l2ca_grp_add_member_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
1255107120Sjulian{
1256107120Sjulian	return (ENOTSUP);
1257107120Sjulian} /* ng_l2cap_l2ca_grp_add_member_req */
1258107120Sjulian
1259107120Sjulian/*
1260107120Sjulian * Send L2CA_GroupAddMember response to the upper layer protocol.
1261107120Sjulian * XXX FIXME
1262107120Sjulian */
1263107120Sjulian
1264107120Sjulianint
1265107120Sjulianng_l2cap_l2ca_grp_add_member_rsp(ng_l2cap_chan_p ch, u_int32_t token,
1266107120Sjulian		u_int16_t result)
1267107120Sjulian{
1268107120Sjulian	return (0);
1269107120Sjulian} /* ng_l2cap_l2ca_grp_add_member_rsp */
1270107120Sjulian
1271107120Sjulian/*
1272107120Sjulian * Process L2CA_GroupDeleteMember request from the upper layer protocol
1273107120Sjulian * XXX FIXME
1274107120Sjulian */
1275107120Sjulian
1276107120Sjulianint
1277107120Sjulianng_l2cap_l2ca_grp_rem_member(ng_l2cap_p l2cap, struct ng_mesg *msg)
1278107120Sjulian{
1279107120Sjulian	return (ENOTSUP);
1280107120Sjulian} /* ng_l2cap_l2ca_grp_rem_member */
1281107120Sjulian
1282107120Sjulian/*
1283107120Sjulian * Process L2CA_GroupGetMembers request from the upper layer protocol
1284107120Sjulian * XXX FIXME
1285107120Sjulian */
1286107120Sjulian
1287107120Sjulianint
1288107120Sjulianng_l2cap_l2ca_grp_get_members(ng_l2cap_p l2cap, struct ng_mesg *msg)
1289107120Sjulian{
1290107120Sjulian	return (ENOTSUP);
1291107120Sjulian} /* ng_l2cap_l2ca_grp_get_members */
1292107120Sjulian
1293107120Sjulian/*
1294107120Sjulian * Process L2CA_Ping request from the upper layer protocol
1295107120Sjulian */
1296107120Sjulian
1297107120Sjulianint
1298107120Sjulianng_l2cap_l2ca_ping_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
1299107120Sjulian{
1300107120Sjulian	ng_l2cap_l2ca_ping_ip	*ip = NULL;
1301107120Sjulian	ng_l2cap_con_p		 con = NULL;
1302107120Sjulian	ng_l2cap_cmd_p		 cmd = NULL;
1303107120Sjulian	int			 error = 0;
1304107120Sjulian
1305107120Sjulian	/* Verify message */
1306107120Sjulian	if (msg->header.arglen < sizeof(*ip)) {
1307107120Sjulian		NG_L2CAP_ALERT(
1308107120Sjulian"%s: %s - invalid L2CA_Ping request message size, size=%d\n",
1309107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
1310107120Sjulian			msg->header.arglen);
1311107120Sjulian		error = EMSGSIZE;
1312107120Sjulian		goto out;
1313107120Sjulian	}
1314107120Sjulian
1315107120Sjulian	ip = (ng_l2cap_l2ca_ping_ip *)(msg->data);
1316107120Sjulian	if (ip->echo_size > NG_L2CAP_MAX_ECHO_SIZE) {
1317107120Sjulian		NG_L2CAP_WARN(
1318107120Sjulian"%s: %s - invalid L2CA_Ping request. Echo size is too big, echo_size=%d\n",
1319107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ip->echo_size);
1320107120Sjulian		error = EMSGSIZE;
1321107120Sjulian		goto out;
1322107120Sjulian	}
1323107120Sjulian
1324107120Sjulian	/* Check if we have connection to the unit */
1325107120Sjulian	con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
1326107120Sjulian	if (con == NULL) {
1327107120Sjulian		/* Submit LP_ConnectReq to the lower layer */
1328107120Sjulian		error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr);
1329107120Sjulian		if (error != 0) {
1330107120Sjulian			NG_L2CAP_ERR(
1331107120Sjulian"%s: %s - unable to send LP_ConnectReq message, error=%d\n",
1332107120Sjulian				__func__, NG_NODE_NAME(l2cap->node), error);
1333107120Sjulian			goto out;
1334107120Sjulian		}
1335107120Sjulian
1336107120Sjulian		/* This should not fail */
1337107120Sjulian		con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
1338107120Sjulian		KASSERT((con != NULL),
1339107120Sjulian("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node)));
1340107120Sjulian	}
1341107120Sjulian
1342107120Sjulian	/* Create L2CAP command descriptor */
1343107120Sjulian	cmd = ng_l2cap_new_cmd(con, NULL, ng_l2cap_get_ident(con),
1344107120Sjulian			NG_L2CAP_ECHO_REQ, msg->header.token);
1345107120Sjulian	if (cmd == NULL) {
1346107120Sjulian		error = ENOMEM;
1347107120Sjulian		goto out;
1348107120Sjulian	}
1349107120Sjulian
1350107120Sjulian	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
1351107120Sjulian		ng_l2cap_free_cmd(cmd);
1352107120Sjulian                error = EIO;
1353107120Sjulian		goto out;
1354107120Sjulian	}
1355107120Sjulian
1356107120Sjulian	/* Create L2CAP command packet */
1357107120Sjulian	_ng_l2cap_echo_req(cmd->aux, cmd->ident,
1358107120Sjulian			msg->data + sizeof(*ip), ip->echo_size);
1359107120Sjulian	if (cmd->aux == NULL) {
1360107120Sjulian		ng_l2cap_free_cmd(cmd);
1361107120Sjulian                error = ENOBUFS;
1362107120Sjulian		goto out;
1363107120Sjulian	}
1364107120Sjulian
1365107120Sjulian        /* Link command to the queue */
1366107120Sjulian        ng_l2cap_link_cmd(con, cmd);
1367107120Sjulian	ng_l2cap_lp_deliver(con);
1368107120Sjulianout:
1369107120Sjulian	return (error);
1370107120Sjulian} /* ng_l2cap_l2ca_ping_req */
1371107120Sjulian
1372107120Sjulian/*
1373107120Sjulian * Send L2CA_Ping response to the upper layer protocol
1374107120Sjulian */
1375107120Sjulian
1376107120Sjulianint
1377107120Sjulianng_l2cap_l2ca_ping_rsp(ng_l2cap_con_p con, u_int32_t token, u_int16_t result,
1378107120Sjulian		struct mbuf *data)
1379107120Sjulian{
1380107120Sjulian	ng_l2cap_p		 l2cap = con->l2cap;
1381107120Sjulian	struct ng_mesg		*msg = NULL;
1382107120Sjulian	ng_l2cap_l2ca_ping_op	*op = NULL;
1383107120Sjulian	int			 error = 0, size = 0;
1384107120Sjulian
1385107120Sjulian	/* Check if control hook is connected and valid */
1386107120Sjulian	if (l2cap->ctl == NULL || NG_HOOK_NOT_VALID(l2cap->ctl)) {
1387107120Sjulian		NG_L2CAP_WARN(
1388107120Sjulian"%s: %s - unable to send L2CA_Ping response message. " \
1389107120Sjulian"Hook is not connected or valid\n",
1390107120Sjulian			__func__, NG_NODE_NAME(l2cap->node));
1391107120Sjulian		error = ENOTCONN;
1392107120Sjulian		goto out;
1393107120Sjulian	}
1394107120Sjulian
1395107120Sjulian	size = (data == NULL)? 0 : data->m_pkthdr.len;
1396107120Sjulian
1397107120Sjulian	/* Create and send L2CA_Ping response message */
1398107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_PING,
1399107120Sjulian		sizeof(*op) + size, M_NOWAIT);
1400107120Sjulian	if (msg == NULL)
1401107120Sjulian		error = ENOMEM;
1402107120Sjulian	else {
1403107120Sjulian		msg->header.token = token;
1404107120Sjulian		msg->header.flags |= NGF_RESP;
1405107120Sjulian
1406107120Sjulian		op = (ng_l2cap_l2ca_ping_op *)(msg->data);
1407107120Sjulian		op->result = result;
1408107120Sjulian		bcopy(&con->remote, &op->bdaddr, sizeof(op->bdaddr));
1409107120Sjulian		if (data != NULL && size > 0) {
1410107120Sjulian			op->echo_size = size;
1411107120Sjulian			m_copydata(data, 0, size, (caddr_t) op + sizeof(*op));
1412107120Sjulian		}
1413107120Sjulian
1414128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, 0);
1415107120Sjulian	}
1416107120Sjulianout:
1417107120Sjulian	NG_FREE_M(data);
1418107120Sjulian
1419107120Sjulian	return (error);
1420107120Sjulian} /* ng_l2cap_l2ca_ping_rsp */
1421107120Sjulian
1422107120Sjulian/*
1423107120Sjulian * Process L2CA_GetInfo request from the upper layer protocol
1424107120Sjulian */
1425107120Sjulian
1426107120Sjulianint
1427107120Sjulianng_l2cap_l2ca_get_info_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
1428107120Sjulian{
1429107120Sjulian	ng_l2cap_l2ca_get_info_ip	*ip = NULL;
1430107120Sjulian	ng_l2cap_con_p			 con = NULL;
1431107120Sjulian	ng_l2cap_cmd_p			 cmd = NULL;
1432107120Sjulian	int				 error = 0;
1433107120Sjulian
1434107120Sjulian	/* Verify message */
1435107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
1436107120Sjulian		NG_L2CAP_ALERT(
1437107120Sjulian"%s: %s - invalid L2CA_GetInfo request message size, size=%d\n",
1438107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
1439107120Sjulian			msg->header.arglen);
1440107120Sjulian		error = EMSGSIZE;
1441107120Sjulian		goto out;
1442107120Sjulian	}
1443107120Sjulian
1444107120Sjulian	ip = (ng_l2cap_l2ca_get_info_ip *)(msg->data);
1445107120Sjulian
1446107120Sjulian	/* Check if we have connection to the unit */
1447107120Sjulian	con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
1448107120Sjulian	if (con == NULL) {
1449107120Sjulian		/* Submit LP_ConnectReq to the lower layer */
1450107120Sjulian		error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr);
1451107120Sjulian		if (error != 0) {
1452107120Sjulian			NG_L2CAP_ERR(
1453107120Sjulian"%s: %s - unable to send LP_ConnectReq message, error=%d\n",
1454107120Sjulian				__func__, NG_NODE_NAME(l2cap->node), error);
1455107120Sjulian			goto out;
1456107120Sjulian		}
1457107120Sjulian
1458107120Sjulian		/* This should not fail */
1459107120Sjulian		con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
1460107120Sjulian		KASSERT((con != NULL),
1461107120Sjulian("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node)));
1462107120Sjulian	}
1463107120Sjulian
1464107120Sjulian	/* Create L2CAP command descriptor */
1465107120Sjulian	cmd = ng_l2cap_new_cmd(con, NULL, ng_l2cap_get_ident(con),
1466107120Sjulian			NG_L2CAP_INFO_REQ, msg->header.token);
1467107120Sjulian	if (cmd == NULL) {
1468107120Sjulian		error = ENOMEM;
1469107120Sjulian		goto out;
1470107120Sjulian	}
1471107120Sjulian
1472107120Sjulian	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
1473107120Sjulian		ng_l2cap_free_cmd(cmd);
1474107120Sjulian		error = EIO;
1475107120Sjulian		goto out;
1476107120Sjulian	}
1477107120Sjulian
1478107120Sjulian	/* Create L2CAP command packet */
1479107120Sjulian	_ng_l2cap_info_req(cmd->aux, cmd->ident, ip->info_type);
1480107120Sjulian	if (cmd->aux == NULL) {
1481107120Sjulian		ng_l2cap_free_cmd(cmd);
1482107120Sjulian		error = ENOBUFS;
1483107120Sjulian		goto out;
1484107120Sjulian	}
1485107120Sjulian
1486107120Sjulian        /* Link command to the queue */
1487107120Sjulian	ng_l2cap_link_cmd(con, cmd);
1488107120Sjulian	ng_l2cap_lp_deliver(con);
1489107120Sjulianout:
1490107120Sjulian	return (error);
1491107120Sjulian} /* ng_l2cap_l2ca_get_info_req */
1492107120Sjulian
1493107120Sjulian/*
1494107120Sjulian * Send L2CA_GetInfo response to the upper layer protocol
1495107120Sjulian */
1496107120Sjulian
1497107120Sjulianint
1498107120Sjulianng_l2cap_l2ca_get_info_rsp(ng_l2cap_con_p con, u_int32_t token,
1499107120Sjulian		u_int16_t result, struct mbuf *data)
1500107120Sjulian{
1501107120Sjulian	ng_l2cap_p			 l2cap = con->l2cap;
1502107120Sjulian	struct ng_mesg			*msg = NULL;
1503107120Sjulian	ng_l2cap_l2ca_get_info_op	*op = NULL;
1504107120Sjulian	int				 error = 0, size;
1505107120Sjulian
1506107120Sjulian	/* Check if control hook is connected and valid */
1507107120Sjulian	if (l2cap->ctl == NULL || NG_HOOK_NOT_VALID(l2cap->ctl)) {
1508107120Sjulian		NG_L2CAP_WARN(
1509107120Sjulian"%s: %s - unable to send L2CA_GetInfo response message. " \
1510107120Sjulian"Hook is not connected or valid\n",
1511107120Sjulian			__func__, NG_NODE_NAME(l2cap->node));
1512107120Sjulian		error = ENOTCONN;
1513107120Sjulian		goto out;
1514107120Sjulian	}
1515107120Sjulian
1516107120Sjulian	size = (data == NULL)? 0 : data->m_pkthdr.len;
1517107120Sjulian
1518107120Sjulian	/* Create and send L2CA_GetInfo response message */
1519107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_GET_INFO,
1520107120Sjulian		sizeof(*op) + size, M_NOWAIT);
1521107120Sjulian	if (msg == NULL)
1522107120Sjulian		error = ENOMEM;
1523107120Sjulian	else {
1524107120Sjulian		msg->header.token = token;
1525107120Sjulian		msg->header.flags |= NGF_RESP;
1526107120Sjulian
1527107120Sjulian		op = (ng_l2cap_l2ca_get_info_op *)(msg->data);
1528107120Sjulian		op->result = result;
1529107120Sjulian		if (data != NULL && size > 0) {
1530107120Sjulian			op->info_size = size;
1531107120Sjulian			m_copydata(data, 0, size, (caddr_t) op + sizeof(*op));
1532107120Sjulian		}
1533107120Sjulian
1534128076Semax		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, 0);
1535107120Sjulian	}
1536107120Sjulianout:
1537107120Sjulian	NG_FREE_M(data);
1538107120Sjulian
1539107120Sjulian	return (error);
1540107120Sjulian} /* ng_l2cap_l2ca_get_info_rsp */
1541107120Sjulian
1542107120Sjulian/*
1543107120Sjulian * Process L2CA_EnableCLT message from the upper layer protocol
1544107120Sjulian * XXX convert to NGN_L2CAP_NODE_SET_FLAGS?
1545107120Sjulian */
1546107120Sjulian
1547107120Sjulianint
1548107120Sjulianng_l2cap_l2ca_enable_clt(ng_l2cap_p l2cap, struct ng_mesg *msg)
1549107120Sjulian{
1550107120Sjulian	ng_l2cap_l2ca_enable_clt_ip	*ip = NULL;
1551107120Sjulian	int				 error = 0;
1552107120Sjulian#if 0
1553107120Sjulian *	ng_l2cap_l2ca_enable_clt_op	*op = NULL;
1554107120Sjulian *	u_int16_t			 result;
1555107120Sjulian * 	u_int32_t			 token;
1556107120Sjulian#endif
1557107120Sjulian
1558107120Sjulian	/* Check message */
1559107120Sjulian	if (msg->header.arglen != sizeof(*ip)) {
1560107120Sjulian		NG_L2CAP_ALERT(
1561107120Sjulian"%s: %s - invalid L2CA_EnableCLT message size, size=%d\n",
1562107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
1563107120Sjulian			msg->header.arglen);
1564107120Sjulian
1565107120Sjulian		return (EMSGSIZE);
1566107120Sjulian	}
1567107120Sjulian
1568107120Sjulian	/* Process request */
1569107120Sjulian	ip = (ng_l2cap_l2ca_enable_clt_ip *) (msg->data);
1570107120Sjulian#if 0
1571107120Sjulian *	result = NG_L2CAP_SUCCESS;
1572107120Sjulian#endif
1573107120Sjulian
1574107120Sjulian	switch (ip->psm)
1575107120Sjulian	{
1576107120Sjulian	case 0:
1577107120Sjulian		/* Special case: disable/enable all PSM */
1578107120Sjulian		if (ip->enable)
1579107120Sjulian			l2cap->flags &= ~(NG_L2CAP_CLT_SDP_DISABLED    |
1580107120Sjulian					  NG_L2CAP_CLT_RFCOMM_DISABLED |
1581107120Sjulian					  NG_L2CAP_CLT_TCP_DISABLED);
1582107120Sjulian		else
1583107120Sjulian			l2cap->flags |= (NG_L2CAP_CLT_SDP_DISABLED    |
1584107120Sjulian					 NG_L2CAP_CLT_RFCOMM_DISABLED |
1585107120Sjulian					 NG_L2CAP_CLT_TCP_DISABLED);
1586107120Sjulian		break;
1587107120Sjulian
1588107120Sjulian	case NG_L2CAP_PSM_SDP:
1589107120Sjulian		if (ip->enable)
1590107120Sjulian			l2cap->flags &= ~NG_L2CAP_CLT_SDP_DISABLED;
1591107120Sjulian		else
1592107120Sjulian			l2cap->flags |= NG_L2CAP_CLT_SDP_DISABLED;
1593107120Sjulian		break;
1594107120Sjulian
1595107120Sjulian	case NG_L2CAP_PSM_RFCOMM:
1596107120Sjulian		if (ip->enable)
1597107120Sjulian			l2cap->flags &= ~NG_L2CAP_CLT_RFCOMM_DISABLED;
1598107120Sjulian		else
1599107120Sjulian			l2cap->flags |= NG_L2CAP_CLT_RFCOMM_DISABLED;
1600107120Sjulian		break;
1601107120Sjulian
1602107120Sjulian	case NG_L2CAP_PSM_TCP:
1603107120Sjulian		if (ip->enable)
1604107120Sjulian			l2cap->flags &= ~NG_L2CAP_CLT_TCP_DISABLED;
1605107120Sjulian		else
1606107120Sjulian			l2cap->flags |= NG_L2CAP_CLT_TCP_DISABLED;
1607107120Sjulian		break;
1608107120Sjulian
1609107120Sjulian	default:
1610107120Sjulian		NG_L2CAP_ERR(
1611107120Sjulian"%s: %s - unsupported PSM=%d\n", __func__, NG_NODE_NAME(l2cap->node), ip->psm);
1612107120Sjulian#if 0
1613107120Sjulian *		result = NG_L2CAP_PSM_NOT_SUPPORTED;
1614107120Sjulian#endif
1615107120Sjulian		error = ENOTSUP;
1616107120Sjulian		break;
1617107120Sjulian	}
1618107120Sjulian
1619107120Sjulian#if 0
1620107120Sjulian *	/* Create and send response message */
1621107120Sjulian * 	token = msg->header.token;
1622107120Sjulian * 	NG_FREE_MSG(msg);
1623107120Sjulian * 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_ENABLE_CLT,
1624107120Sjulian * 		sizeof(*op), M_NOWAIT);
1625107120Sjulian * 	if (msg == NULL)
1626107120Sjulian * 		error = ENOMEM;
1627107120Sjulian * 	else {
1628107120Sjulian * 		msg->header.token = token;
1629107120Sjulian * 		msg->header.flags |= NGF_RESP;
1630107120Sjulian *
1631107120Sjulian * 		op = (ng_l2cap_l2ca_enable_clt_op *)(msg->data);
1632107120Sjulian * 		op->result = result;
1633107120Sjulian * 	}
1634107120Sjulian *
1635107120Sjulian * 	/* Send response to control hook */
1636107120Sjulian * 	if (l2cap->ctl != NULL && NG_HOOK_IS_VALID(l2cap->ctl))
1637128076Semax * 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, 0);
1638107120Sjulian#endif
1639107120Sjulian
1640107120Sjulian	return (error);
1641107120Sjulian} /* ng_l2cap_l2ca_enable_clt */
1642107120Sjulian
1643