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