1107120Sjulian/*
2107120Sjulian * ng_hci_misc.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 *
30121054Semax * $Id: ng_hci_misc.c,v 1.5 2003/09/08 18:57:51 max Exp $
31107120Sjulian * $FreeBSD$
32107120Sjulian */
33107120Sjulian
34107120Sjulian#include <sys/param.h>
35107120Sjulian#include <sys/systm.h>
36107120Sjulian#include <sys/kernel.h>
37107120Sjulian#include <sys/malloc.h>
38107120Sjulian#include <sys/mbuf.h>
39107120Sjulian#include <sys/queue.h>
40107120Sjulian#include <netgraph/ng_message.h>
41107120Sjulian#include <netgraph/netgraph.h>
42128688Semax#include <netgraph/bluetooth/include/ng_bluetooth.h>
43128688Semax#include <netgraph/bluetooth/include/ng_hci.h>
44128688Semax#include <netgraph/bluetooth/hci/ng_hci_var.h>
45128688Semax#include <netgraph/bluetooth/hci/ng_hci_cmds.h>
46128688Semax#include <netgraph/bluetooth/hci/ng_hci_evnt.h>
47128688Semax#include <netgraph/bluetooth/hci/ng_hci_ulpi.h>
48128688Semax#include <netgraph/bluetooth/hci/ng_hci_misc.h>
49107120Sjulian
50107120Sjulian/******************************************************************************
51107120Sjulian ******************************************************************************
52107120Sjulian **                              Utility routines
53107120Sjulian ******************************************************************************
54107120Sjulian ******************************************************************************/
55107120Sjulian
56107120Sjulian/*
57107120Sjulian * Give packet to RAW hook
58107120Sjulian * Assumes input mbuf is read only.
59107120Sjulian */
60107120Sjulian
61107120Sjulianvoid
62107120Sjulianng_hci_mtap(ng_hci_unit_p unit, struct mbuf *m0)
63107120Sjulian{
64107120Sjulian	struct mbuf	*m = NULL;
65107120Sjulian	int		 error = 0;
66107120Sjulian
67107120Sjulian	if (unit->raw != NULL && NG_HOOK_IS_VALID(unit->raw)) {
68243882Sglebius		m = m_dup(m0, M_NOWAIT);
69107120Sjulian		if (m != NULL)
70107120Sjulian			NG_SEND_DATA_ONLY(error, unit->raw, m);
71107120Sjulian
72107120Sjulian		if (error != 0)
73107120Sjulian			NG_HCI_INFO(
74107120Sjulian"%s: %s - Could not forward packet, error=%d\n",
75107120Sjulian				__func__, NG_NODE_NAME(unit->node), error);
76107120Sjulian	}
77107120Sjulian} /* ng_hci_mtap */
78107120Sjulian
79107120Sjulian/*
80107120Sjulian * Send notification to the upper layer's
81107120Sjulian */
82107120Sjulian
83107120Sjulianvoid
84107120Sjulianng_hci_node_is_up(node_p node, hook_p hook, void *arg1, int arg2)
85107120Sjulian{
86107120Sjulian	ng_hci_unit_p		 unit = NULL;
87107120Sjulian	struct ng_mesg		*msg = NULL;
88107120Sjulian	ng_hci_node_up_ep	*ep = NULL;
89107120Sjulian	int			 error;
90107120Sjulian
91107120Sjulian	if (node == NULL || NG_NODE_NOT_VALID(node) ||
92107120Sjulian	    hook == NULL || NG_HOOK_NOT_VALID(hook))
93107120Sjulian		return;
94107120Sjulian
95107120Sjulian	unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
96107120Sjulian	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY)
97107120Sjulian		return;
98107120Sjulian
99107120Sjulian	if (hook != unit->acl && hook != unit->sco)
100107120Sjulian		return;
101107120Sjulian
102107120Sjulian	NG_MKMESSAGE(msg,NGM_HCI_COOKIE,NGM_HCI_NODE_UP,sizeof(*ep),M_NOWAIT);
103107120Sjulian	if (msg != NULL) {
104107120Sjulian		ep = (ng_hci_node_up_ep *)(msg->data);
105107120Sjulian
106107120Sjulian		if (hook == unit->acl) {
107107120Sjulian			NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->pkt_size);
108107120Sjulian			NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->num_pkts);
109107120Sjulian		} else {
110107120Sjulian			NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->pkt_size);
111107120Sjulian			NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->num_pkts);
112107120Sjulian		}
113107120Sjulian
114107120Sjulian		bcopy(&unit->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
115107120Sjulian
116128076Semax		NG_SEND_MSG_HOOK(error, node, msg, hook, 0);
117107120Sjulian	} else
118107120Sjulian		error = ENOMEM;
119107120Sjulian
120107120Sjulian	if (error != 0)
121107120Sjulian		NG_HCI_INFO(
122107120Sjulian"%s: %s - failed to send NODE_UP message to hook \"%s\", error=%d\n",
123107120Sjulian			__func__, NG_NODE_NAME(unit->node),
124107120Sjulian			NG_HOOK_NAME(hook), error);
125107120Sjulian} /* ng_hci_node_is_up */
126107120Sjulian
127107120Sjulian/*
128107120Sjulian * Clean unit (helper)
129107120Sjulian */
130107120Sjulian
131107120Sjulianvoid
132107120Sjulianng_hci_unit_clean(ng_hci_unit_p unit, int reason)
133107120Sjulian{
134107120Sjulian	int	size;
135107120Sjulian
136107120Sjulian	/* Drain command queue */
137107120Sjulian	if (unit->state & NG_HCI_UNIT_COMMAND_PENDING)
138107120Sjulian		ng_hci_command_untimeout(unit);
139107120Sjulian
140107120Sjulian	NG_BT_MBUFQ_DRAIN(&unit->cmdq);
141107120Sjulian	NG_HCI_BUFF_CMD_SET(unit->buffer, 1);
142107120Sjulian
143107120Sjulian	/* Clean up connection list */
144107120Sjulian	while (!LIST_EMPTY(&unit->con_list)) {
145107120Sjulian		ng_hci_unit_con_p	con = LIST_FIRST(&unit->con_list);
146107120Sjulian
147121054Semax		/* Remove all timeouts (if any) */
148121054Semax		if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
149121054Semax			ng_hci_con_untimeout(con);
150121054Semax
151107120Sjulian		/*
152107120Sjulian		 * Notify upper layer protocol and destroy connection
153107120Sjulian		 * descriptor. Do not really care about the result.
154107120Sjulian		 */
155107120Sjulian
156107120Sjulian		ng_hci_lp_discon_ind(con, reason);
157107120Sjulian		ng_hci_free_con(con);
158107120Sjulian	}
159107120Sjulian
160107120Sjulian	NG_HCI_BUFF_ACL_TOTAL(unit->buffer, size);
161107120Sjulian	NG_HCI_BUFF_ACL_FREE(unit->buffer, size);
162107120Sjulian
163107120Sjulian	NG_HCI_BUFF_SCO_TOTAL(unit->buffer, size);
164107120Sjulian	NG_HCI_BUFF_SCO_FREE(unit->buffer, size);
165107120Sjulian
166107120Sjulian	/* Clean up neighbors list */
167107120Sjulian	ng_hci_flush_neighbor_cache(unit);
168107120Sjulian} /* ng_hci_unit_clean */
169107120Sjulian
170107120Sjulian/*
171107120Sjulian * Allocate and link new unit neighbor cache entry
172107120Sjulian */
173107120Sjulian
174107120Sjulianng_hci_neighbor_p
175107120Sjulianng_hci_new_neighbor(ng_hci_unit_p unit)
176107120Sjulian{
177107120Sjulian	ng_hci_neighbor_p	n = NULL;
178107120Sjulian
179184205Sdes	n = malloc(sizeof(*n), M_NETGRAPH_HCI,
180107120Sjulian		M_NOWAIT | M_ZERO);
181107120Sjulian	if (n != NULL) {
182107120Sjulian		getmicrotime(&n->updated);
183107120Sjulian		LIST_INSERT_HEAD(&unit->neighbors, n, next);
184107120Sjulian	}
185107120Sjulian
186107120Sjulian	return (n);
187107120Sjulian} /* ng_hci_new_neighbor */
188107120Sjulian
189107120Sjulian/*
190107120Sjulian * Free unit neighbor cache entry
191107120Sjulian */
192107120Sjulian
193107120Sjulianvoid
194107120Sjulianng_hci_free_neighbor(ng_hci_neighbor_p n)
195107120Sjulian{
196107120Sjulian	LIST_REMOVE(n, next);
197107120Sjulian	bzero(n, sizeof(*n));
198184205Sdes	free(n, M_NETGRAPH_HCI);
199107120Sjulian} /* ng_hci_free_neighbor */
200107120Sjulian
201107120Sjulian/*
202107120Sjulian * Flush neighbor cache
203107120Sjulian */
204107120Sjulian
205107120Sjulianvoid
206107120Sjulianng_hci_flush_neighbor_cache(ng_hci_unit_p unit)
207107120Sjulian{
208107120Sjulian	while (!LIST_EMPTY(&unit->neighbors))
209107120Sjulian		ng_hci_free_neighbor(LIST_FIRST(&unit->neighbors));
210107120Sjulian} /* ng_hci_flush_neighbor_cache */
211107120Sjulian
212107120Sjulian/*
213107120Sjulian * Lookup unit in neighbor cache
214107120Sjulian */
215107120Sjulian
216107120Sjulianng_hci_neighbor_p
217107120Sjulianng_hci_get_neighbor(ng_hci_unit_p unit, bdaddr_p bdaddr)
218107120Sjulian{
219107120Sjulian	ng_hci_neighbor_p	n = NULL;
220107120Sjulian
221107120Sjulian	for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) {
222107120Sjulian		ng_hci_neighbor_p	nn = LIST_NEXT(n, next);
223107120Sjulian
224107120Sjulian		if (!ng_hci_neighbor_stale(n)) {
225107120Sjulian			if (bcmp(&n->bdaddr, bdaddr, sizeof(*bdaddr)) == 0)
226107120Sjulian				break;
227107120Sjulian		} else
228107120Sjulian			ng_hci_free_neighbor(n); /* remove old entry */
229107120Sjulian
230107120Sjulian		n = nn;
231107120Sjulian	}
232107120Sjulian
233107120Sjulian	return (n);
234107120Sjulian} /* ng_hci_get_neighbor */
235107120Sjulian
236107120Sjulian/*
237107120Sjulian * Check if neighbor entry is stale
238107120Sjulian */
239107120Sjulian
240107120Sjulianint
241107120Sjulianng_hci_neighbor_stale(ng_hci_neighbor_p n)
242107120Sjulian{
243107120Sjulian	struct timeval	now;
244107120Sjulian
245107120Sjulian	getmicrotime(&now);
246107120Sjulian
247107120Sjulian	return (now.tv_sec - n->updated.tv_sec > bluetooth_hci_max_neighbor_age());
248107120Sjulian} /* ng_hci_neighbor_stale */
249107120Sjulian
250107120Sjulian/*
251107120Sjulian * Allocate and link new connection descriptor
252107120Sjulian */
253107120Sjulian
254107120Sjulianng_hci_unit_con_p
255107120Sjulianng_hci_new_con(ng_hci_unit_p unit, int link_type)
256107120Sjulian{
257107120Sjulian	ng_hci_unit_con_p	con = NULL;
258107120Sjulian	int			num_pkts;
259121054Semax	static int		fake_con_handle = 0x0f00;
260107120Sjulian
261184205Sdes	con = malloc(sizeof(*con), M_NETGRAPH_HCI,
262107120Sjulian		M_NOWAIT | M_ZERO);
263107120Sjulian	if (con != NULL) {
264107120Sjulian		con->unit = unit;
265107120Sjulian		con->state = NG_HCI_CON_CLOSED;
266121054Semax
267121054Semax		/*
268121054Semax		 * XXX
269121054Semax		 *
270121054Semax		 * Assign fake connection handle to the connection descriptor.
271121054Semax		 * Bluetooth specification marks 0x0f00 - 0x0fff connection
272121054Semax		 * handles as reserved. We need this fake connection handles
273121054Semax		 * for timeouts. Connection handle will be passed as argument
274121054Semax		 * to timeout so when timeout happens we can find the right
275121054Semax		 * connection descriptor. We can not pass pointers, because
276121054Semax		 * timeouts are external (to Netgraph) events and there might
277121054Semax		 * be a race when node/hook goes down and timeout event already
278121054Semax		 * went into node's queue
279121054Semax		 */
280121054Semax
281121054Semax		con->con_handle = fake_con_handle ++;
282121054Semax		if (fake_con_handle > 0x0fff)
283121054Semax			fake_con_handle = 0x0f00;
284121054Semax
285107120Sjulian		con->link_type = link_type;
286107120Sjulian
287107120Sjulian		if (con->link_type == NG_HCI_LINK_ACL)
288107120Sjulian			NG_HCI_BUFF_ACL_TOTAL(unit->buffer, num_pkts);
289107120Sjulian		else
290107120Sjulian			NG_HCI_BUFF_SCO_TOTAL(unit->buffer, num_pkts);
291107120Sjulian
292107120Sjulian		NG_BT_ITEMQ_INIT(&con->conq, num_pkts);
293107120Sjulian
294137163Semax		ng_callout_init(&con->con_timo);
295107120Sjulian
296107120Sjulian		LIST_INSERT_HEAD(&unit->con_list, con, next);
297107120Sjulian	}
298107120Sjulian
299107120Sjulian	return (con);
300107120Sjulian} /* ng_hci_new_con */
301107120Sjulian
302107120Sjulian/*
303107120Sjulian * Free connection descriptor
304107120Sjulian */
305107120Sjulian
306107120Sjulianvoid
307107120Sjulianng_hci_free_con(ng_hci_unit_con_p con)
308107120Sjulian{
309107120Sjulian	LIST_REMOVE(con, next);
310107120Sjulian
311107120Sjulian	/*
312107120Sjulian	 * If we have pending packets then assume that Host Controller has
313107120Sjulian	 * flushed these packets and we can free them too
314107120Sjulian	 */
315107120Sjulian
316107120Sjulian	if (con->link_type == NG_HCI_LINK_ACL)
317107120Sjulian		NG_HCI_BUFF_ACL_FREE(con->unit->buffer, con->pending);
318107120Sjulian	else
319107120Sjulian		NG_HCI_BUFF_SCO_FREE(con->unit->buffer, con->pending);
320107120Sjulian
321107120Sjulian	NG_BT_ITEMQ_DESTROY(&con->conq);
322107120Sjulian
323107120Sjulian	bzero(con, sizeof(*con));
324184205Sdes	free(con, M_NETGRAPH_HCI);
325107120Sjulian} /* ng_hci_free_con */
326107120Sjulian
327107120Sjulian/*
328107120Sjulian * Lookup connection for given unit and connection handle.
329107120Sjulian */
330107120Sjulian
331107120Sjulianng_hci_unit_con_p
332107120Sjulianng_hci_con_by_handle(ng_hci_unit_p unit, int con_handle)
333107120Sjulian{
334107120Sjulian	ng_hci_unit_con_p	con = NULL;
335107120Sjulian
336107120Sjulian	LIST_FOREACH(con, &unit->con_list, next)
337107120Sjulian		if (con->con_handle == con_handle)
338107120Sjulian			break;
339107120Sjulian
340107120Sjulian	return (con);
341107120Sjulian} /* ng_hci_con_by_handle */
342107120Sjulian
343107120Sjulian/*
344107120Sjulian * Lookup connection for given unit, link type and remove unit address
345107120Sjulian */
346107120Sjulian
347107120Sjulianng_hci_unit_con_p
348107120Sjulianng_hci_con_by_bdaddr(ng_hci_unit_p unit, bdaddr_p bdaddr, int link_type)
349107120Sjulian{
350107120Sjulian	ng_hci_unit_con_p	con = NULL;
351107120Sjulian
352107120Sjulian	LIST_FOREACH(con, &unit->con_list, next)
353107120Sjulian		if (con->link_type == link_type &&
354107120Sjulian		    bcmp(&con->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
355107120Sjulian			break;
356107120Sjulian
357107120Sjulian	return (con);
358107120Sjulian} /* ng_hci_con_by_bdaddr */
359107120Sjulian
360107120Sjulian/*
361107120Sjulian * Set HCI command timeout
362138268Sglebius * XXX FIXME: check return code from ng_callout
363107120Sjulian */
364107120Sjulian
365121054Semaxint
366107120Sjulianng_hci_command_timeout(ng_hci_unit_p unit)
367107120Sjulian{
368121054Semax	if (unit->state & NG_HCI_UNIT_COMMAND_PENDING)
369121054Semax		panic(
370121054Semax"%s: %s - Duplicated command timeout!\n", __func__, NG_NODE_NAME(unit->node));
371121054Semax
372121054Semax	unit->state |= NG_HCI_UNIT_COMMAND_PENDING;
373138268Sglebius	ng_callout(&unit->cmd_timo, unit->node, NULL,
374121054Semax				bluetooth_hci_command_timeout(),
375121054Semax				ng_hci_process_command_timeout, NULL, 0);
376121054Semax
377121054Semax	return (0);
378107120Sjulian} /* ng_hci_command_timeout */
379107120Sjulian
380107120Sjulian/*
381107120Sjulian * Unset HCI command timeout
382107120Sjulian */
383107120Sjulian
384121054Semaxint
385107120Sjulianng_hci_command_untimeout(ng_hci_unit_p unit)
386107120Sjulian{
387121054Semax	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
388121054Semax		panic(
389121054Semax"%s: %s - No command timeout!\n", __func__, NG_NODE_NAME(unit->node));
390107120Sjulian
391138268Sglebius	if (ng_uncallout(&unit->cmd_timo, unit->node) == 0)
392121054Semax		return (ETIMEDOUT);
393107120Sjulian
394121054Semax	unit->state &= ~NG_HCI_UNIT_COMMAND_PENDING;
395107120Sjulian
396121054Semax	return (0);
397121054Semax} /* ng_hci_command_untimeout */
398107120Sjulian
399107120Sjulian/*
400107120Sjulian * Set HCI connection timeout
401138268Sglebius * XXX FIXME: check return code from ng_callout
402107120Sjulian */
403107120Sjulian
404121054Semaxint
405107120Sjulianng_hci_con_timeout(ng_hci_unit_con_p con)
406107120Sjulian{
407121054Semax	if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
408121054Semax		panic(
409121054Semax"%s: %s - Duplicated connection timeout!\n",
410121054Semax			__func__, NG_NODE_NAME(con->unit->node));
411121054Semax
412121054Semax	con->flags |= NG_HCI_CON_TIMEOUT_PENDING;
413138268Sglebius	ng_callout(&con->con_timo, con->unit->node, NULL,
414121054Semax				bluetooth_hci_connect_timeout(),
415121054Semax				ng_hci_process_con_timeout, NULL,
416121054Semax				con->con_handle);
417121054Semax
418121054Semax	return (0);
419107120Sjulian} /* ng_hci_con_timeout */
420107120Sjulian
421107120Sjulian/*
422107120Sjulian * Unset HCI connection timeout
423107120Sjulian */
424107120Sjulian
425121054Semaxint
426107120Sjulianng_hci_con_untimeout(ng_hci_unit_con_p con)
427107120Sjulian{
428121054Semax	if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING))
429121054Semax		panic(
430121054Semax"%s: %s - No connection timeout!\n", __func__, NG_NODE_NAME(con->unit->node));
431107120Sjulian
432138268Sglebius	if (ng_uncallout(&con->con_timo, con->unit->node) == 0)
433121054Semax		return (ETIMEDOUT);
434107120Sjulian
435121054Semax	con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
436107120Sjulian
437121054Semax	return (0);
438121054Semax} /* ng_hci_con_untimeout */
439107120Sjulian
440107120Sjulian#if 0
441107120Sjulian/*
442107120Sjulian * Convert numeric error code/reason to a string
443107120Sjulian */
444107120Sjulian
445107120Sjulianchar const * const
446107120Sjulianng_hci_str_error(u_int16_t code)
447107120Sjulian{
448107120Sjulian#define	LAST_ERROR_CODE			((sizeof(s)/sizeof(s[0]))-1)
449107120Sjulian	static char const * const	s[] = {
450107120Sjulian	/* 0x00 */ "No error",
451107120Sjulian	/* 0x01 */ "Unknown HCI command",
452107120Sjulian	/* 0x02 */ "No connection",
453107120Sjulian	/* 0x03 */ "Hardware failure",
454107120Sjulian	/* 0x04 */ "Page timeout",
455107120Sjulian	/* 0x05 */ "Authentication failure",
456107120Sjulian	/* 0x06 */ "Key missing",
457107120Sjulian	/* 0x07 */ "Memory full",
458107120Sjulian	/* 0x08 */ "Connection timeout",
459107120Sjulian	/* 0x09 */ "Max number of connections",
460107120Sjulian	/* 0x0a */ "Max number of SCO connections to a unit",
461107120Sjulian	/* 0x0b */ "ACL connection already exists",
462107120Sjulian	/* 0x0c */ "Command disallowed",
463107120Sjulian	/* 0x0d */ "Host rejected due to limited resources",
464107120Sjulian	/* 0x0e */ "Host rejected due to securiity reasons",
465107120Sjulian	/* 0x0f */ "Host rejected due to remote unit is a personal unit",
466107120Sjulian	/* 0x10 */ "Host timeout",
467107120Sjulian	/* 0x11 */ "Unsupported feature or parameter value",
468107120Sjulian	/* 0x12 */ "Invalid HCI command parameter",
469107120Sjulian	/* 0x13 */ "Other end terminated connection: User ended connection",
470107120Sjulian	/* 0x14 */ "Other end terminated connection: Low resources",
471107120Sjulian	/* 0x15 */ "Other end terminated connection: About to power off",
472107120Sjulian	/* 0x16 */ "Connection terminated by local host",
473107120Sjulian	/* 0x17 */ "Repeated attempts",
474107120Sjulian	/* 0x18 */ "Pairing not allowed",
475107120Sjulian	/* 0x19 */ "Unknown LMP PDU",
476107120Sjulian	/* 0x1a */ "Unsupported remote feature",
477107120Sjulian	/* 0x1b */ "SCO offset rejected",
478107120Sjulian	/* 0x1c */ "SCO interval rejected",
479107120Sjulian	/* 0x1d */ "SCO air mode rejected",
480107120Sjulian	/* 0x1e */ "Invalid LMP parameters",
481107120Sjulian	/* 0x1f */ "Unspecified error",
482107120Sjulian	/* 0x20 */ "Unsupported LMP parameter value",
483107120Sjulian	/* 0x21 */ "Role change not allowed",
484107120Sjulian	/* 0x22 */ "LMP response timeout",
485107120Sjulian	/* 0x23 */ "LMP error transaction collision",
486107120Sjulian	/* 0x24 */ "LMP PSU not allowed",
487107120Sjulian	/* 0x25 */ "Encryption mode not acceptable",
488107120Sjulian	/* 0x26 */ "Unit key used",
489107120Sjulian	/* 0x27 */ "QoS is not supported",
490107120Sjulian	/* 0x28 */ "Instant passed",
491107120Sjulian	/* 0x29 */ "Paring with unit key not supported",
492107120Sjulian	/* SHOULD ALWAYS BE LAST */ "Unknown error"
493107120Sjulian	};
494107120Sjulian
495107120Sjulian	return ((code >= LAST_ERROR_CODE)? s[LAST_ERROR_CODE] : s[code]);
496107120Sjulian} /* ng_hci_str_error */
497107120Sjulian#endif
498107120Sjulian
499