xref: /freebsd/sys/netgraph/bluetooth/hci/ng_hci_misc.c (revision f9218d3d4fd34f082473b3a021c6d4d109fb47cf)
1 /*
2  * ng_hci_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_hci_misc.c,v 1.18 2002/10/30 00:18:19 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_hci_var.h"
43 #include "ng_hci_cmds.h"
44 #include "ng_hci_evnt.h"
45 #include "ng_hci_ulpi.h"
46 #include "ng_hci_misc.h"
47 
48 /******************************************************************************
49  ******************************************************************************
50  **                              Utility routines
51  ******************************************************************************
52  ******************************************************************************/
53 
54 static void	ng_hci_command_queue_timeout		(void *);
55 static void	ng_hci_con_queue_timeout		(void *);
56 static void	ng_hci_con_queue_watchdog_timeout	(void *);
57 
58 /*
59  * Give packet to RAW hook
60  * Assumes input mbuf is read only.
61  */
62 
63 void
64 ng_hci_mtap(ng_hci_unit_p unit, struct mbuf *m0)
65 {
66 	struct mbuf	*m = NULL;
67 	int		 error = 0;
68 
69 	if (unit->raw != NULL && NG_HOOK_IS_VALID(unit->raw)) {
70 		m = m_dup(m0, M_DONTWAIT);
71 		if (m != NULL)
72 			NG_SEND_DATA_ONLY(error, unit->raw, m);
73 
74 		if (error != 0)
75 			NG_HCI_INFO(
76 "%s: %s - Could not forward packet, error=%d\n",
77 				__func__, NG_NODE_NAME(unit->node), error);
78 	}
79 } /* ng_hci_mtap */
80 
81 /*
82  * Send notification to the upper layer's
83  */
84 
85 void
86 ng_hci_node_is_up(node_p node, hook_p hook, void *arg1, int arg2)
87 {
88 	ng_hci_unit_p		 unit = NULL;
89 	struct ng_mesg		*msg = NULL;
90 	ng_hci_node_up_ep	*ep = NULL;
91 	int			 error;
92 
93 	if (node == NULL || NG_NODE_NOT_VALID(node) ||
94 	    hook == NULL || NG_HOOK_NOT_VALID(hook))
95 		return;
96 
97 	unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
98 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY)
99 		return;
100 
101 	if (hook != unit->acl && hook != unit->sco)
102 		return;
103 
104 	NG_MKMESSAGE(msg,NGM_HCI_COOKIE,NGM_HCI_NODE_UP,sizeof(*ep),M_NOWAIT);
105 	if (msg != NULL) {
106 		ep = (ng_hci_node_up_ep *)(msg->data);
107 
108 		if (hook == unit->acl) {
109 			NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->pkt_size);
110 			NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->num_pkts);
111 		} else {
112 			NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->pkt_size);
113 			NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->num_pkts);
114 		}
115 
116 		bcopy(&unit->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
117 
118 		NG_SEND_MSG_HOOK(error, node, msg, hook, NULL);
119 	} else
120 		error = ENOMEM;
121 
122 	if (error != 0)
123 		NG_HCI_INFO(
124 "%s: %s - failed to send NODE_UP message to hook \"%s\", error=%d\n",
125 			__func__, NG_NODE_NAME(unit->node),
126 			NG_HOOK_NAME(hook), error);
127 } /* ng_hci_node_is_up */
128 
129 /*
130  * Clean unit (helper)
131  */
132 
133 void
134 ng_hci_unit_clean(ng_hci_unit_p unit, int reason)
135 {
136 	int	size;
137 
138 	/* Drain command queue */
139 	if (unit->state & NG_HCI_UNIT_COMMAND_PENDING)
140 		ng_hci_command_untimeout(unit);
141 
142 	NG_BT_MBUFQ_DRAIN(&unit->cmdq);
143 	NG_HCI_BUFF_CMD_SET(unit->buffer, 1);
144 
145 	/* Clean up connection list */
146 	while (!LIST_EMPTY(&unit->con_list)) {
147 		ng_hci_unit_con_p	con = LIST_FIRST(&unit->con_list);
148 
149 		/*
150 		 * Notify upper layer protocol and destroy connection
151 		 * descriptor. Do not really care about the result.
152 		 */
153 
154 		ng_hci_lp_discon_ind(con, reason);
155 		ng_hci_free_con(con);
156 	}
157 
158 	NG_HCI_BUFF_ACL_TOTAL(unit->buffer, size);
159 	NG_HCI_BUFF_ACL_FREE(unit->buffer, size);
160 
161 	NG_HCI_BUFF_SCO_TOTAL(unit->buffer, size);
162 	NG_HCI_BUFF_SCO_FREE(unit->buffer, size);
163 
164 	/* Clean up neighbors list */
165 	ng_hci_flush_neighbor_cache(unit);
166 } /* ng_hci_unit_clean */
167 
168 /*
169  * Allocate and link new unit neighbor cache entry
170  */
171 
172 ng_hci_neighbor_p
173 ng_hci_new_neighbor(ng_hci_unit_p unit)
174 {
175 	ng_hci_neighbor_p	n = NULL;
176 
177 	MALLOC(n, ng_hci_neighbor_p, sizeof(*n), M_NETGRAPH_HCI,
178 		M_NOWAIT | M_ZERO);
179 	if (n != NULL) {
180 		getmicrotime(&n->updated);
181 		LIST_INSERT_HEAD(&unit->neighbors, n, next);
182 	}
183 
184 	return (n);
185 } /* ng_hci_new_neighbor */
186 
187 /*
188  * Free unit neighbor cache entry
189  */
190 
191 void
192 ng_hci_free_neighbor(ng_hci_neighbor_p n)
193 {
194 	LIST_REMOVE(n, next);
195 	bzero(n, sizeof(*n));
196 	FREE(n, M_NETGRAPH_HCI);
197 } /* ng_hci_free_neighbor */
198 
199 /*
200  * Flush neighbor cache
201  */
202 
203 void
204 ng_hci_flush_neighbor_cache(ng_hci_unit_p unit)
205 {
206 	while (!LIST_EMPTY(&unit->neighbors))
207 		ng_hci_free_neighbor(LIST_FIRST(&unit->neighbors));
208 } /* ng_hci_flush_neighbor_cache */
209 
210 /*
211  * Lookup unit in neighbor cache
212  */
213 
214 ng_hci_neighbor_p
215 ng_hci_get_neighbor(ng_hci_unit_p unit, bdaddr_p bdaddr)
216 {
217 	ng_hci_neighbor_p	n = NULL;
218 
219 	for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) {
220 		ng_hci_neighbor_p	nn = LIST_NEXT(n, next);
221 
222 		if (!ng_hci_neighbor_stale(n)) {
223 			if (bcmp(&n->bdaddr, bdaddr, sizeof(*bdaddr)) == 0)
224 				break;
225 		} else
226 			ng_hci_free_neighbor(n); /* remove old entry */
227 
228 		n = nn;
229 	}
230 
231 	return (n);
232 } /* ng_hci_get_neighbor */
233 
234 /*
235  * Check if neighbor entry is stale
236  */
237 
238 int
239 ng_hci_neighbor_stale(ng_hci_neighbor_p n)
240 {
241 	struct timeval	now;
242 
243 	getmicrotime(&now);
244 
245 	return (now.tv_sec - n->updated.tv_sec > bluetooth_hci_max_neighbor_age());
246 } /* ng_hci_neighbor_stale */
247 
248 /*
249  * Allocate and link new connection descriptor
250  */
251 
252 ng_hci_unit_con_p
253 ng_hci_new_con(ng_hci_unit_p unit, int link_type)
254 {
255 	ng_hci_unit_con_p	con = NULL;
256 	int			num_pkts;
257 
258 	MALLOC(con, ng_hci_unit_con_p, sizeof(*con), M_NETGRAPH_HCI,
259 		M_NOWAIT | M_ZERO);
260 	if (con != NULL) {
261 		con->unit = unit;
262 		con->state = NG_HCI_CON_CLOSED;
263 		con->link_type = link_type;
264 
265 		if (con->link_type == NG_HCI_LINK_ACL)
266 			NG_HCI_BUFF_ACL_TOTAL(unit->buffer, num_pkts);
267 		else
268 			NG_HCI_BUFF_SCO_TOTAL(unit->buffer, num_pkts);
269 
270 		NG_BT_ITEMQ_INIT(&con->conq, num_pkts);
271 
272 		callout_handle_init(&con->con_timo);
273 		callout_handle_init(&con->watchdog_timo);
274 
275 		LIST_INSERT_HEAD(&unit->con_list, con, next);
276 	}
277 
278 	return (con);
279 } /* ng_hci_new_con */
280 
281 /*
282  * Free connection descriptor
283  */
284 
285 void
286 ng_hci_free_con(ng_hci_unit_con_p con)
287 {
288 	LIST_REMOVE(con, next);
289 
290 	/* Remove all timeouts (if any) */
291 	if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
292 		ng_hci_con_untimeout(con);
293 
294 	if (con->flags & NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING)
295 		ng_hci_con_watchdog_untimeout(con);
296 
297 	/*
298 	 * If we have pending packets then assume that Host Controller has
299 	 * flushed these packets and we can free them too
300 	 */
301 
302 	if (con->link_type == NG_HCI_LINK_ACL)
303 		NG_HCI_BUFF_ACL_FREE(con->unit->buffer, con->pending);
304 	else
305 		NG_HCI_BUFF_SCO_FREE(con->unit->buffer, con->pending);
306 
307 	NG_BT_ITEMQ_DESTROY(&con->conq);
308 
309 	bzero(con, sizeof(*con));
310 	FREE(con, M_NETGRAPH_HCI);
311 } /* ng_hci_free_con */
312 
313 /*
314  * Lookup connection for given unit and connection handle.
315  */
316 
317 ng_hci_unit_con_p
318 ng_hci_con_by_handle(ng_hci_unit_p unit, int con_handle)
319 {
320 	ng_hci_unit_con_p	con = NULL;
321 
322 	LIST_FOREACH(con, &unit->con_list, next)
323 		if (con->con_handle == con_handle)
324 			break;
325 
326 	return (con);
327 } /* ng_hci_con_by_handle */
328 
329 /*
330  * Lookup connection for given unit, link type and remove unit address
331  */
332 
333 ng_hci_unit_con_p
334 ng_hci_con_by_bdaddr(ng_hci_unit_p unit, bdaddr_p bdaddr, int link_type)
335 {
336 	ng_hci_unit_con_p	con = NULL;
337 
338 	LIST_FOREACH(con, &unit->con_list, next)
339 		if (con->link_type == link_type &&
340 		    bcmp(&con->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
341 			break;
342 
343 	return (con);
344 } /* ng_hci_con_by_bdaddr */
345 
346 /*
347  * Set HCI command timeout
348  */
349 
350 void
351 ng_hci_command_timeout(ng_hci_unit_p unit)
352 {
353 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) {
354 		NG_NODE_REF(unit->node);
355 		unit->state |= NG_HCI_UNIT_COMMAND_PENDING;
356 		unit->cmd_timo = timeout(ng_hci_command_queue_timeout, unit,
357 					bluetooth_hci_command_timeout());
358 	} else
359 		KASSERT(0,
360 ("%s: %s - Duplicated command timeout!\n", __func__, NG_NODE_NAME(unit->node)));
361 } /* ng_hci_command_timeout */
362 
363 /*
364  * Unset HCI command timeout
365  */
366 
367 void
368 ng_hci_command_untimeout(ng_hci_unit_p unit)
369 {
370 	if (unit->state & NG_HCI_UNIT_COMMAND_PENDING) {
371 		unit->state &= ~NG_HCI_UNIT_COMMAND_PENDING;
372 		untimeout(ng_hci_command_queue_timeout, unit, unit->cmd_timo);
373 		NG_NODE_UNREF(unit->node);
374 	} else
375 		KASSERT(0,
376 ("%s: %s - No command timeout!\n", __func__, NG_NODE_NAME(unit->node)));
377 } /* ng_hci_command_untimeout */
378 
379 /*
380  * OK timeout has happend, so queue timeout processing function
381  */
382 
383 static void
384 ng_hci_command_queue_timeout(void *context)
385 {
386 	ng_hci_unit_p	unit = (ng_hci_unit_p) context;
387 	node_p		node = unit->node;
388 
389 	if (NG_NODE_IS_VALID(node))
390 		ng_send_fn(node,NULL,&ng_hci_process_command_timeout,unit,0);
391 
392 	NG_NODE_UNREF(node);
393 } /* ng_hci_command_queue_timeout */
394 
395 /*
396  * Set HCI connection timeout
397  */
398 
399 void
400 ng_hci_con_timeout(ng_hci_unit_con_p con)
401 {
402 	if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) {
403 		NG_NODE_REF(con->unit->node);
404 		con->flags |= NG_HCI_CON_TIMEOUT_PENDING;
405 		con->con_timo = timeout(ng_hci_con_queue_timeout, con,
406 					bluetooth_hci_connect_timeout());
407 	} else
408 		KASSERT(0,
409 ("%s: %s - Duplicated connection timeout!\n",
410 			__func__, NG_NODE_NAME(con->unit->node)));
411 } /* ng_hci_con_timeout */
412 
413 /*
414  * Unset HCI connection timeout
415  */
416 
417 void
418 ng_hci_con_untimeout(ng_hci_unit_con_p con)
419 {
420 	if (con->flags & NG_HCI_CON_TIMEOUT_PENDING) {
421 		con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
422 		untimeout(ng_hci_con_queue_timeout, con, con->con_timo);
423 		NG_NODE_UNREF(con->unit->node);
424 	} else
425 		KASSERT(0,
426 ("%s: %s - No connection timeout!\n", __func__, NG_NODE_NAME(con->unit->node)));
427 } /* ng_hci_con_untimeout */
428 
429 /*
430  * OK timeout has happend, so queue timeout processing function
431  */
432 
433 static void
434 ng_hci_con_queue_timeout(void *context)
435 {
436 	ng_hci_unit_con_p	con = (ng_hci_unit_con_p) context;
437 	node_p			node = con->unit->node;
438 
439 	if (NG_NODE_IS_VALID(node))
440 		ng_send_fn(node, NULL, &ng_hci_process_con_timeout, con, 0);
441 
442 	NG_NODE_UNREF(node);
443 } /* ng_hci_con_queue_timeout */
444 
445 /*
446  * Set HCI connection watchdog timeout
447  */
448 
449 void
450 ng_hci_con_watchdog_timeout(ng_hci_unit_con_p con)
451 {
452 	if (!(con->flags & NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING)) {
453 		NG_NODE_REF(con->unit->node);
454 		con->flags |= NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING;
455 		con->watchdog_timo = timeout(ng_hci_con_queue_watchdog_timeout,
456 					con, bluetooth_hci_watchdog_timeout());
457 	} else
458 		KASSERT(0,
459 ("%s: %s - Duplicated connection watchdog timeout!\n",
460 			__func__, NG_NODE_NAME(con->unit->node)));
461 } /* ng_hci_con_watchdog_timeout */
462 
463 /*
464  * Unset HCI connection watchdog timeout
465  */
466 
467 void
468 ng_hci_con_watchdog_untimeout(ng_hci_unit_con_p con)
469 {
470 	if (con->flags & NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING) {
471 		con->flags &= ~NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING;
472 		untimeout(ng_hci_con_queue_watchdog_timeout, con,
473 			con->watchdog_timo);
474 		NG_NODE_UNREF(con->unit->node);
475 	} else
476 		KASSERT(0,
477 ("%s: %s - No connection watchdog timeout!\n",
478 			 __func__, NG_NODE_NAME(con->unit->node)));
479 } /* ng_hci_con_watchdog_untimeout */
480 
481 /*
482  * OK timeout has happend, so queue timeout processing function
483  */
484 
485 static void
486 ng_hci_con_queue_watchdog_timeout(void *context)
487 {
488 	ng_hci_unit_con_p	con = (ng_hci_unit_con_p) context;
489 	node_p			node = con->unit->node;
490 
491 	if (NG_NODE_IS_VALID(node))
492 		ng_send_fn(node, NULL, &ng_hci_process_con_watchdog_timeout,
493 			con, 0);
494 
495 	NG_NODE_UNREF(node);
496 } /* ng_hci_con_queue_watchdog_timeout */
497 
498 #if 0
499 /*
500  * Convert numeric error code/reason to a string
501  */
502 
503 char const * const
504 ng_hci_str_error(u_int16_t code)
505 {
506 #define	LAST_ERROR_CODE			((sizeof(s)/sizeof(s[0]))-1)
507 	static char const * const	s[] = {
508 	/* 0x00 */ "No error",
509 	/* 0x01 */ "Unknown HCI command",
510 	/* 0x02 */ "No connection",
511 	/* 0x03 */ "Hardware failure",
512 	/* 0x04 */ "Page timeout",
513 	/* 0x05 */ "Authentication failure",
514 	/* 0x06 */ "Key missing",
515 	/* 0x07 */ "Memory full",
516 	/* 0x08 */ "Connection timeout",
517 	/* 0x09 */ "Max number of connections",
518 	/* 0x0a */ "Max number of SCO connections to a unit",
519 	/* 0x0b */ "ACL connection already exists",
520 	/* 0x0c */ "Command disallowed",
521 	/* 0x0d */ "Host rejected due to limited resources",
522 	/* 0x0e */ "Host rejected due to securiity reasons",
523 	/* 0x0f */ "Host rejected due to remote unit is a personal unit",
524 	/* 0x10 */ "Host timeout",
525 	/* 0x11 */ "Unsupported feature or parameter value",
526 	/* 0x12 */ "Invalid HCI command parameter",
527 	/* 0x13 */ "Other end terminated connection: User ended connection",
528 	/* 0x14 */ "Other end terminated connection: Low resources",
529 	/* 0x15 */ "Other end terminated connection: About to power off",
530 	/* 0x16 */ "Connection terminated by local host",
531 	/* 0x17 */ "Repeated attempts",
532 	/* 0x18 */ "Pairing not allowed",
533 	/* 0x19 */ "Unknown LMP PDU",
534 	/* 0x1a */ "Unsupported remote feature",
535 	/* 0x1b */ "SCO offset rejected",
536 	/* 0x1c */ "SCO interval rejected",
537 	/* 0x1d */ "SCO air mode rejected",
538 	/* 0x1e */ "Invalid LMP parameters",
539 	/* 0x1f */ "Unspecified error",
540 	/* 0x20 */ "Unsupported LMP parameter value",
541 	/* 0x21 */ "Role change not allowed",
542 	/* 0x22 */ "LMP response timeout",
543 	/* 0x23 */ "LMP error transaction collision",
544 	/* 0x24 */ "LMP PSU not allowed",
545 	/* 0x25 */ "Encryption mode not acceptable",
546 	/* 0x26 */ "Unit key used",
547 	/* 0x27 */ "QoS is not supported",
548 	/* 0x28 */ "Instant passed",
549 	/* 0x29 */ "Paring with unit key not supported",
550 	/* SHOULD ALWAYS BE LAST */ "Unknown error"
551 	};
552 
553 	return ((code >= LAST_ERROR_CODE)? s[LAST_ERROR_CODE] : s[code]);
554 } /* ng_hci_str_error */
555 #endif
556 
557