xref: /freebsd/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.c (revision f9218d3d4fd34f082473b3a021c6d4d109fb47cf)
1 /*
2  * ng_l2cap_misc.c
3  *
4  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $Id: ng_l2cap_misc.c,v 1.16 2002/09/04 21:38:38 max Exp $
29  * $FreeBSD$
30  */
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/mbuf.h>
37 #include <sys/queue.h>
38 #include <netgraph/ng_message.h>
39 #include <netgraph/netgraph.h>
40 #include "ng_bluetooth.h"
41 #include "ng_hci.h"
42 #include "ng_l2cap.h"
43 #include "ng_l2cap_var.h"
44 #include "ng_l2cap_cmds.h"
45 #include "ng_l2cap_evnt.h"
46 #include "ng_l2cap_llpi.h"
47 #include "ng_l2cap_ulpi.h"
48 #include "ng_l2cap_misc.h"
49 
50 static u_int16_t	ng_l2cap_get_cid		(ng_l2cap_p);
51 static void		ng_l2cap_queue_lp_timeout	(void *);
52 static void		ng_l2cap_queue_command_timeout	(void *);
53 
54 /******************************************************************************
55  ******************************************************************************
56  **                              Utility routines
57  ******************************************************************************
58  ******************************************************************************/
59 
60 /*
61  * Send hook information to the upper layer
62  */
63 
64 void
65 ng_l2cap_send_hook_info(node_p node, hook_p hook, void *arg1, int arg2)
66 {
67 	ng_l2cap_p	 l2cap = NULL;
68 	struct ng_mesg	*msg = NULL;
69 	int		 error = 0;
70 
71 	if (node == NULL || NG_NODE_NOT_VALID(node) ||
72 	    hook == NULL || NG_HOOK_NOT_VALID(hook))
73 		return;
74 
75 	l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
76 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci) ||
77 	    bcmp(&l2cap->bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2cap->bdaddr)) == 0)
78 		return;
79 
80 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_HOOK_INFO,
81 		sizeof(bdaddr_t), M_NOWAIT);
82 	if (msg != NULL) {
83 		bcopy(&l2cap->bdaddr, msg->data, sizeof(bdaddr_t));
84 		NG_SEND_MSG_HOOK(error, node, msg, hook, NULL);
85 	} else
86 		error = ENOMEM;
87 
88 	if (error != 0)
89 		NG_L2CAP_INFO(
90 "%s: %s - failed to send HOOK_INFO message to hook \"%s\", error=%d\n",
91 			__func__, NG_NODE_NAME(l2cap->node), NG_HOOK_NAME(hook),
92 			error);
93 } /* ng_l2cap_send_hook_info */
94 
95 /*
96  * Create new connection descriptor for the "remote" unit. Will create new
97  * connection descriptor and signal channel. Will link both connection and
98  * channel to the l2cap node.
99  */
100 
101 ng_l2cap_con_p
102 ng_l2cap_new_con(ng_l2cap_p l2cap, bdaddr_p bdaddr)
103 {
104 	ng_l2cap_con_p	con = NULL;
105 
106 	/* Create new connection descriptor */
107 	MALLOC(con, ng_l2cap_con_p, sizeof(*con), M_NETGRAPH_L2CAP,
108 		M_NOWAIT|M_ZERO);
109 	if (con == NULL)
110 		return (NULL);
111 
112 	con->l2cap = l2cap;
113 	con->state = NG_L2CAP_CON_CLOSED;
114 
115 	bcopy(bdaddr, &con->remote, sizeof(con->remote));
116 	callout_handle_init(&con->con_timo);
117 
118 	con->ident = NG_L2CAP_FIRST_IDENT - 1;
119 	TAILQ_INIT(&con->cmd_list);
120 
121 	/* Link connection */
122 	LIST_INSERT_HEAD(&l2cap->con_list, con, next);
123 
124 	return (con);
125 } /* ng_l2cap_new_con */
126 
127 /*
128  * Free connection descriptor. Will unlink connection and free everything.
129  */
130 
131 void
132 ng_l2cap_free_con(ng_l2cap_con_p con)
133 {
134 	ng_l2cap_chan_p f = NULL, n = NULL;
135 
136 	if (con->state == NG_L2CAP_W4_LP_CON_CFM)
137 		ng_l2cap_lp_untimeout(con);
138 
139 	if (con->tx_pkt != NULL) {
140 		while (con->tx_pkt != NULL) {
141 			struct mbuf	*m = con->tx_pkt->m_nextpkt;
142 
143 			m_freem(con->tx_pkt);
144 			con->tx_pkt = m;
145 		}
146 	}
147 
148 	NG_FREE_M(con->rx_pkt);
149 
150 	for (f = LIST_FIRST(&con->l2cap->chan_list); f != NULL; ) {
151 		n = LIST_NEXT(f, next);
152 
153 		if (f->con == con)
154 			ng_l2cap_free_chan(f);
155 
156 		f = n;
157 	}
158 
159 	while (!TAILQ_EMPTY(&con->cmd_list)) {
160 		ng_l2cap_cmd_p	cmd = TAILQ_FIRST(&con->cmd_list);
161 
162 		ng_l2cap_unlink_cmd(cmd);
163 		ng_l2cap_free_cmd(cmd);
164 	}
165 
166 	LIST_REMOVE(con, next);
167 	bzero(con, sizeof(*con));
168 	FREE(con, M_NETGRAPH_L2CAP);
169 } /* ng_l2cap_free_con */
170 
171 /*
172  * Get connection by "remote" address
173  */
174 
175 ng_l2cap_con_p
176 ng_l2cap_con_by_addr(ng_l2cap_p l2cap, bdaddr_p bdaddr)
177 {
178 	ng_l2cap_con_p	con = NULL;
179 
180 	LIST_FOREACH(con, &l2cap->con_list, next)
181 		if (bcmp(bdaddr, &con->remote, sizeof(con->remote)) == 0)
182 			break;
183 
184 	return (con);
185 } /* ng_l2cap_con_by_addr */
186 
187 /*
188  * Get connection by "handle"
189  */
190 
191 ng_l2cap_con_p
192 ng_l2cap_con_by_handle(ng_l2cap_p l2cap, u_int16_t con_handle)
193 {
194 	ng_l2cap_con_p	con = NULL;
195 
196 	LIST_FOREACH(con, &l2cap->con_list, next)
197 		if (con->con_handle == con_handle)
198 			break;
199 
200 	return (con);
201 } /* ng_l2cap_con_by_handle */
202 
203 /*
204  * Allocate new L2CAP channel descriptor on "con" conection with "psm".
205  * Will link the channel to the l2cap node
206  */
207 
208 ng_l2cap_chan_p
209 ng_l2cap_new_chan(ng_l2cap_p l2cap, ng_l2cap_con_p con, u_int16_t psm)
210 {
211 	ng_l2cap_chan_p	ch = NULL;
212 
213 	MALLOC(ch, ng_l2cap_chan_p, sizeof(*ch), M_NETGRAPH_L2CAP,
214 		M_NOWAIT|M_ZERO);
215 	if (ch == NULL)
216 		return (NULL);
217 
218 	ch->scid = ng_l2cap_get_cid(l2cap);
219 
220 	if (ch->scid != NG_L2CAP_NULL_CID) {
221 		/* Initialize channel */
222 		ch->psm = psm;
223 		ch->con = con;
224 		ch->state = NG_L2CAP_CLOSED;
225 
226 		/* Set MTU and flow control settings to defaults */
227 		ch->imtu = NG_L2CAP_MTU_DEFAULT;
228 		bcopy(ng_l2cap_default_flow(), &ch->iflow, sizeof(ch->iflow));
229 
230 		ch->omtu = NG_L2CAP_MTU_DEFAULT;
231 		bcopy(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow));
232 
233 		ch->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT;
234 		ch->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT;
235 
236 		LIST_INSERT_HEAD(&l2cap->chan_list, ch, next);
237 	} else {
238 		bzero(ch, sizeof(*ch));
239 		FREE(ch, M_NETGRAPH_L2CAP);
240 		ch = NULL;
241 	}
242 
243 	return (ch);
244 } /* ng_l2cap_new_chan */
245 
246 /*
247  * Get channel by source (local) channel ID
248  */
249 
250 ng_l2cap_chan_p
251 ng_l2cap_chan_by_scid(ng_l2cap_p l2cap, u_int16_t scid)
252 {
253 	ng_l2cap_chan_p	ch = NULL;
254 
255 	LIST_FOREACH(ch, &l2cap->chan_list, next)
256 		if (ch->scid == scid)
257 			break;
258 
259 	return (ch);
260 } /* ng_l2cap_chan_by_scid */
261 
262 /*
263  * Free channel descriptor.
264  */
265 
266 void
267 ng_l2cap_free_chan(ng_l2cap_chan_p ch)
268 {
269 	ng_l2cap_cmd_p	f = NULL, n = NULL;
270 
271 	f = TAILQ_FIRST(&ch->con->cmd_list);
272 	while (f != NULL) {
273 		n = TAILQ_NEXT(f, next);
274 
275 		if (f->ch == ch) {
276 			ng_l2cap_unlink_cmd(f);
277 			ng_l2cap_free_cmd(f);
278 		}
279 
280 		f = n;
281 	}
282 
283 	LIST_REMOVE(ch, next);
284 	bzero(ch, sizeof(*ch));
285 	FREE(ch, M_NETGRAPH_L2CAP);
286 } /* ng_l2cap_free_chan */
287 
288 /*
289  * Create new L2CAP command descriptor. WILL NOT add command to the queue.
290  */
291 
292 ng_l2cap_cmd_p
293 ng_l2cap_new_cmd(ng_l2cap_con_p con, ng_l2cap_chan_p ch, u_int8_t ident,
294 		u_int8_t code, u_int32_t token)
295 {
296 	ng_l2cap_cmd_p	cmd = NULL;
297 
298 	KASSERT((ch == NULL || ch->con == con),
299 ("%s: %s - invalid channel pointer!\n",
300 		__func__, NG_NODE_NAME(con->l2cap->node)));
301 
302 	MALLOC(cmd, ng_l2cap_cmd_p, sizeof(*cmd), M_NETGRAPH_L2CAP,
303 		M_NOWAIT|M_ZERO);
304 	if (cmd == NULL)
305 		return (NULL);
306 
307 	cmd->con = con;
308 	cmd->ch = ch;
309 	cmd->ident = ident;
310 	cmd->code = code;
311 	cmd->token = token;
312 	callout_handle_init(&cmd->timo);
313 
314 	return (cmd);
315 } /* ng_l2cap_new_cmd */
316 
317 /*
318  * Get L2CAP command descriptor by ident
319  */
320 
321 ng_l2cap_cmd_p
322 ng_l2cap_cmd_by_ident(ng_l2cap_con_p con, u_int8_t ident)
323 {
324 	ng_l2cap_cmd_p	cmd = NULL;
325 
326 	TAILQ_FOREACH(cmd, &con->cmd_list, next)
327 		if (cmd->ident == ident)
328 			break;
329 
330 	return (cmd);
331 } /* ng_l2cap_cmd_by_ident */
332 
333 /*
334  * Set LP timeout
335  */
336 
337 void
338 ng_l2cap_lp_timeout(ng_l2cap_con_p con)
339 {
340 	NG_NODE_REF(con->l2cap->node);
341 	con->con_timo = timeout(ng_l2cap_queue_lp_timeout, con,
342 				bluetooth_hci_connect_timeout());
343 } /* ng_l2cap_lp_timeout */
344 
345 /*
346  * Unset LP timeout
347  */
348 
349 void
350 ng_l2cap_lp_untimeout(ng_l2cap_con_p con)
351 {
352 	untimeout(ng_l2cap_queue_lp_timeout, con, con->con_timo);
353 	NG_NODE_UNREF(con->l2cap->node);
354 } /* ng_l2cap_lp_untimeout */
355 
356 /*
357  * OK, timeout has happend so queue LP timeout processing function
358  */
359 
360 static void
361 ng_l2cap_queue_lp_timeout(void *context)
362 {
363 	ng_l2cap_con_p	con = (ng_l2cap_con_p) context;
364 	node_p		node = con->l2cap->node;
365 
366 	/*
367 	 * We need to save node pointer here, because ng_send_fn()
368 	 * can execute ng_l2cap_process_lp_timeout() without putting
369 	 * item into node's queue (if node can be locked). Once
370 	 * ng_l2cap_process_lp_timeout() executed the con pointer
371 	 * is no longer valid.
372 	 */
373 
374 	if (NG_NODE_IS_VALID(node))
375 		ng_send_fn(node, NULL, &ng_l2cap_process_lp_timeout, con, 0);
376 
377 	NG_NODE_UNREF(node);
378 } /* ng_l2cap_queue_lp_timeout */
379 
380 /*
381  * Set L2CAP command timeout
382  */
383 
384 void
385 ng_l2cap_command_timeout(ng_l2cap_cmd_p cmd, int timo)
386 {
387 	NG_NODE_REF(cmd->con->l2cap->node);
388 	cmd->flags |= NG_L2CAP_CMD_PENDING;
389 	cmd->timo = timeout(ng_l2cap_queue_command_timeout, cmd, timo);
390 } /* ng_l2cap_command_timeout */
391 
392 /*
393  * Unset L2CAP command timeout
394  */
395 
396 void
397 ng_l2cap_command_untimeout(ng_l2cap_cmd_p cmd)
398 {
399 	cmd->flags &= ~NG_L2CAP_CMD_PENDING;
400 	untimeout(ng_l2cap_queue_command_timeout, cmd, cmd->timo);
401 	NG_NODE_UNREF(cmd->con->l2cap->node);
402 } /* ng_l2cap_command_untimeout */
403 
404 /*
405  * OK, timeout has happend so queue L2CAP command timeout processing function
406  */
407 
408 static void
409 ng_l2cap_queue_command_timeout(void *context)
410 {
411 	ng_l2cap_cmd_p	cmd = (ng_l2cap_cmd_p) context;
412 	node_p		node = cmd->con->l2cap->node;
413 
414 	/*
415 	 * We need to save node pointer here, because ng_send_fn()
416 	 * can execute ng_l2cap_process_command_timeout() without
417 	 * putting item into node's queue (if node can be locked).
418 	 * Once ng_l2cap_process_command_timeout() executed the
419 	 * cmd pointer is no longer valid.
420 	 */
421 
422 	if (NG_NODE_IS_VALID(node))
423 		ng_send_fn(node,NULL,&ng_l2cap_process_command_timeout,cmd,0);
424 
425 	NG_NODE_UNREF(node);
426 } /* ng_l2cap_queue_command_timeout */
427 
428 /*
429  * Prepend "m"buf with "size" bytes
430  */
431 
432 struct mbuf *
433 ng_l2cap_prepend(struct mbuf *m, int size)
434 {
435 	M_PREPEND(m, size, M_DONTWAIT);
436 	if (m == NULL || (m->m_len < size && (m = m_pullup(m, size)) == NULL))
437 		return (NULL);
438 
439 	return (m);
440 } /* ng_l2cap_prepend */
441 
442 /*
443  * Default flow settings
444  */
445 
446 ng_l2cap_flow_p
447 ng_l2cap_default_flow(void)
448 {
449 	static ng_l2cap_flow_t	default_flow = {
450 		/* flags */		0x0,
451 		/* service_type */	NG_HCI_SERVICE_TYPE_BEST_EFFORT,
452 		/* token_rate */	0xffffffff, /* maximum */
453 		/* token_bucket_size */	0xffffffff, /* maximum */
454 		/* peak_bandwidth */	0x00000000, /* maximum */
455 		/* latency */		0xffffffff, /* don't care */
456 		/* delay_variation */	0xffffffff  /* don't care */
457 	};
458 
459 	return (&default_flow);
460 } /* ng_l2cap_default_flow */
461 
462 /*
463  * Get next available channel ID
464  * XXX FIXME this is *UGLY* but will do for now
465  */
466 
467 static u_int16_t
468 ng_l2cap_get_cid(ng_l2cap_p l2cap)
469 {
470 	u_int16_t	cid = l2cap->cid + 1;
471 
472 	if (cid < NG_L2CAP_FIRST_CID)
473 		cid = NG_L2CAP_FIRST_CID;
474 
475 	while (cid != l2cap->cid) {
476 		if (ng_l2cap_chan_by_scid(l2cap, cid) == NULL) {
477 			l2cap->cid = cid;
478 
479 			return (cid);
480 		}
481 
482 		cid ++;
483 		if (cid < NG_L2CAP_FIRST_CID)
484 			cid = NG_L2CAP_FIRST_CID;
485 	}
486 
487 	return (NG_L2CAP_NULL_CID);
488 } /* ng_l2cap_get_cid */
489 
490 /*
491  * Get next available command ident
492  * XXX FIXME this is *UGLY* but will do for now
493  */
494 
495 u_int8_t
496 ng_l2cap_get_ident(ng_l2cap_con_p con)
497 {
498 	u_int8_t	ident = con->ident + 1;
499 
500 	if (ident < NG_L2CAP_FIRST_IDENT)
501 		ident = NG_L2CAP_FIRST_IDENT;
502 
503 	while (ident != con->ident) {
504 		if (ng_l2cap_cmd_by_ident(con, ident) == NULL) {
505 			con->ident = ident;
506 
507 			return (ident);
508 		}
509 
510 		ident ++;
511 		if (ident < NG_L2CAP_FIRST_IDENT)
512 			ident = NG_L2CAP_FIRST_IDENT;
513 	}
514 
515 	return (NG_L2CAP_NULL_IDENT);
516 } /* ng_l2cap_get_ident */
517 
518