xref: /dragonfly/sys/netgraph7/bluetooth/hci/ng_hci_misc.c (revision b5523eac31a95e6876e05e20e6fe836ec3a45202)
1 /*
2  * ng_hci_misc.c
3  */
4 
5 /*-
6  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $Id: ng_hci_misc.c,v 1.5 2003/09/08 18:57:51 max Exp $
31  * $FreeBSD: src/sys/netgraph/bluetooth/hci/ng_hci_misc.c,v 1.10 2005/01/07 01:45:43 imp Exp $
32  */
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/mbuf.h>
39 #include <sys/queue.h>
40 #include <netgraph7/ng_message.h>
41 #include <netgraph7/netgraph.h>
42 #include <netgraph7/bluetooth/include/ng_bluetooth.h>
43 #include <netgraph7/bluetooth/include/ng_hci.h>
44 #include <netgraph7/bluetooth/hci/ng_hci_var.h>
45 #include <netgraph7/bluetooth/hci/ng_hci_cmds.h>
46 #include <netgraph7/bluetooth/hci/ng_hci_evnt.h>
47 #include <netgraph7/bluetooth/hci/ng_hci_ulpi.h>
48 #include <netgraph7/bluetooth/hci/ng_hci_misc.h>
49 
50 /******************************************************************************
51  ******************************************************************************
52  **                              Utility routines
53  ******************************************************************************
54  ******************************************************************************/
55 
56 /*
57  * Give packet to RAW hook
58  * Assumes input mbuf is read only.
59  */
60 
61 void
ng_hci_mtap(ng_hci_unit_p unit,struct mbuf * m0)62 ng_hci_mtap(ng_hci_unit_p unit, struct mbuf *m0)
63 {
64           struct mbuf         *m = NULL;
65           int                  error = 0;
66 
67           if (unit->raw != NULL && NG_HOOK_IS_VALID(unit->raw)) {
68                     m = m_dup(m0, M_NOWAIT);
69                     if (m != NULL)
70                               NG_SEND_DATA_ONLY(error, unit->raw, m);
71 
72                     if (error != 0)
73                               NG_HCI_INFO(
74 "%s: %s - Could not forward packet, error=%d\n",
75                                         __func__, NG_NODE_NAME(unit->node), error);
76           }
77 } /* ng_hci_mtap */
78 
79 /*
80  * Send notification to the upper layer's
81  */
82 
83 void
ng_hci_node_is_up(node_p node,hook_p hook,void * arg1,int arg2)84 ng_hci_node_is_up(node_p node, hook_p hook, void *arg1, int arg2)
85 {
86           ng_hci_unit_p                  unit = NULL;
87           struct ng_mesg                *msg = NULL;
88           ng_hci_node_up_ep   *ep = NULL;
89           int                            error;
90 
91           if (node == NULL || NG_NODE_NOT_VALID(node) ||
92               hook == NULL || NG_HOOK_NOT_VALID(hook))
93                     return;
94 
95           unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
96           if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY)
97                     return;
98 
99           if (hook != unit->acl && hook != unit->sco)
100                     return;
101 
102           NG_MKMESSAGE(msg,NGM_HCI_COOKIE,NGM_HCI_NODE_UP,sizeof(*ep),M_WAITOK | M_NULLOK);
103           if (msg != NULL) {
104                     ep = (ng_hci_node_up_ep *)(msg->data);
105 
106                     if (hook == unit->acl) {
107                               NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->pkt_size);
108                               NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->num_pkts);
109                     } else {
110                               NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->pkt_size);
111                               NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->num_pkts);
112                     }
113 
114                     bcopy(&unit->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
115 
116                     NG_SEND_MSG_HOOK(error, node, msg, hook, 0);
117           } else
118                     error = ENOMEM;
119 
120           if (error != 0)
121                     NG_HCI_INFO(
122 "%s: %s - failed to send NODE_UP message to hook \"%s\", error=%d\n",
123                               __func__, NG_NODE_NAME(unit->node),
124                               NG_HOOK_NAME(hook), error);
125 } /* ng_hci_node_is_up */
126 
127 /*
128  * Clean unit (helper)
129  */
130 
131 void
ng_hci_unit_clean(ng_hci_unit_p unit,int reason)132 ng_hci_unit_clean(ng_hci_unit_p unit, int reason)
133 {
134           int       size;
135 
136           /* Drain command queue */
137           if (unit->state & NG_HCI_UNIT_COMMAND_PENDING)
138                     ng_hci_command_untimeout(unit);
139 
140           NG_BT_MBUFQ_DRAIN(&unit->cmdq);
141           NG_HCI_BUFF_CMD_SET(unit->buffer, 1);
142 
143           /* Clean up connection list */
144           while (!LIST_EMPTY(&unit->con_list)) {
145                     ng_hci_unit_con_p   con = LIST_FIRST(&unit->con_list);
146 
147                     /* Remove all timeouts (if any) */
148                     if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
149                               ng_hci_con_untimeout(con);
150 
151                     /*
152                      * Notify upper layer protocol and destroy connection
153                      * descriptor. Do not really care about the result.
154                      */
155 
156                     ng_hci_lp_discon_ind(con, reason);
157                     ng_hci_free_con(con);
158           }
159 
160           NG_HCI_BUFF_ACL_TOTAL(unit->buffer, size);
161           NG_HCI_BUFF_ACL_FREE(unit->buffer, size);
162 
163           NG_HCI_BUFF_SCO_TOTAL(unit->buffer, size);
164           NG_HCI_BUFF_SCO_FREE(unit->buffer, size);
165 
166           /* Clean up neighbors list */
167           ng_hci_flush_neighbor_cache(unit);
168 } /* ng_hci_unit_clean */
169 
170 /*
171  * Allocate and link new unit neighbor cache entry
172  */
173 
174 ng_hci_neighbor_p
ng_hci_new_neighbor(ng_hci_unit_p unit)175 ng_hci_new_neighbor(ng_hci_unit_p unit)
176 {
177           ng_hci_neighbor_p   n = NULL;
178 
179           n = kmalloc(sizeof(*n), M_NETGRAPH_HCI, M_WAITOK | M_NULLOK | M_ZERO);
180           if (n != NULL) {
181                     getmicrotime(&n->updated);
182                     LIST_INSERT_HEAD(&unit->neighbors, n, next);
183           }
184 
185           return (n);
186 } /* ng_hci_new_neighbor */
187 
188 /*
189  * Free unit neighbor cache entry
190  */
191 
192 void
ng_hci_free_neighbor(ng_hci_neighbor_p n)193 ng_hci_free_neighbor(ng_hci_neighbor_p n)
194 {
195           LIST_REMOVE(n, next);
196           bzero(n, sizeof(*n));
197           kfree(n, M_NETGRAPH_HCI);
198 } /* ng_hci_free_neighbor */
199 
200 /*
201  * Flush neighbor cache
202  */
203 
204 void
ng_hci_flush_neighbor_cache(ng_hci_unit_p unit)205 ng_hci_flush_neighbor_cache(ng_hci_unit_p unit)
206 {
207           while (!LIST_EMPTY(&unit->neighbors))
208                     ng_hci_free_neighbor(LIST_FIRST(&unit->neighbors));
209 } /* ng_hci_flush_neighbor_cache */
210 
211 /*
212  * Lookup unit in neighbor cache
213  */
214 
215 ng_hci_neighbor_p
ng_hci_get_neighbor(ng_hci_unit_p unit,bdaddr_p bdaddr)216 ng_hci_get_neighbor(ng_hci_unit_p unit, bdaddr_p bdaddr)
217 {
218           ng_hci_neighbor_p   n = NULL;
219 
220           for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) {
221                     ng_hci_neighbor_p   nn = LIST_NEXT(n, next);
222 
223                     if (!ng_hci_neighbor_stale(n)) {
224                               if (bcmp(&n->bdaddr, bdaddr, sizeof(*bdaddr)) == 0)
225                                         break;
226                     } else
227                               ng_hci_free_neighbor(n); /* remove old entry */
228 
229                     n = nn;
230           }
231 
232           return (n);
233 } /* ng_hci_get_neighbor */
234 
235 /*
236  * Check if neighbor entry is stale
237  */
238 
239 int
ng_hci_neighbor_stale(ng_hci_neighbor_p n)240 ng_hci_neighbor_stale(ng_hci_neighbor_p n)
241 {
242           struct timeval      now;
243 
244           getmicrotime(&now);
245 
246           return (now.tv_sec - n->updated.tv_sec > bluetooth_hci_max_neighbor_age());
247 } /* ng_hci_neighbor_stale */
248 
249 /*
250  * Allocate and link new connection descriptor
251  */
252 
253 ng_hci_unit_con_p
ng_hci_new_con(ng_hci_unit_p unit,int link_type)254 ng_hci_new_con(ng_hci_unit_p unit, int link_type)
255 {
256           ng_hci_unit_con_p   con = NULL;
257           int                           num_pkts;
258           static int                    fake_con_handle = 0x0f00;
259 
260           con = kmalloc(sizeof(*con), M_NETGRAPH_HCI,
261                           M_WAITOK | M_NULLOK | M_ZERO);
262           if (con != NULL) {
263                     con->unit = unit;
264                     con->state = NG_HCI_CON_CLOSED;
265 
266                     /*
267                      * XXX
268                      *
269                      * Assign fake connection handle to the connection descriptor.
270                      * Bluetooth specification marks 0x0f00 - 0x0fff connection
271                      * handles as reserved. We need this fake connection handles
272                      * for timeouts. Connection handle will be passed as argument
273                      * to timeout so when timeout happens we can find the right
274                      * connection descriptor. We can not pass pointers, because
275                      * timeouts are external (to Netgraph) events and there might
276                      * be a race when node/hook goes down and timeout event already
277                      * went into node's queue
278                      */
279 
280                     con->con_handle = fake_con_handle ++;
281                     if (fake_con_handle > 0x0fff)
282                               fake_con_handle = 0x0f00;
283 
284                     con->link_type = link_type;
285 
286                     if (con->link_type == NG_HCI_LINK_ACL)
287                               NG_HCI_BUFF_ACL_TOTAL(unit->buffer, num_pkts);
288                     else
289                               NG_HCI_BUFF_SCO_TOTAL(unit->buffer, num_pkts);
290 
291                     NG_BT_ITEMQ_INIT(&con->conq, num_pkts);
292 
293                     ng_callout_init(&con->con_timo);
294 
295                     LIST_INSERT_HEAD(&unit->con_list, con, next);
296           }
297 
298           return (con);
299 } /* ng_hci_new_con */
300 
301 /*
302  * Free connection descriptor
303  */
304 
305 void
ng_hci_free_con(ng_hci_unit_con_p con)306 ng_hci_free_con(ng_hci_unit_con_p con)
307 {
308           LIST_REMOVE(con, next);
309 
310           /*
311            * If we have pending packets then assume that Host Controller has
312            * flushed these packets and we can free them too
313            */
314 
315           if (con->link_type == NG_HCI_LINK_ACL)
316                     NG_HCI_BUFF_ACL_FREE(con->unit->buffer, con->pending);
317           else
318                     NG_HCI_BUFF_SCO_FREE(con->unit->buffer, con->pending);
319 
320           NG_BT_ITEMQ_DESTROY(&con->conq);
321 
322           bzero(con, sizeof(*con));
323           kfree(con, M_NETGRAPH_HCI);
324 } /* ng_hci_free_con */
325 
326 /*
327  * Lookup connection for given unit and connection handle.
328  */
329 
330 ng_hci_unit_con_p
ng_hci_con_by_handle(ng_hci_unit_p unit,int con_handle)331 ng_hci_con_by_handle(ng_hci_unit_p unit, int con_handle)
332 {
333           ng_hci_unit_con_p   con = NULL;
334 
335           LIST_FOREACH(con, &unit->con_list, next)
336                     if (con->con_handle == con_handle)
337                               break;
338 
339           return (con);
340 } /* ng_hci_con_by_handle */
341 
342 /*
343  * Lookup connection for given unit, link type and remove unit address
344  */
345 
346 ng_hci_unit_con_p
ng_hci_con_by_bdaddr(ng_hci_unit_p unit,bdaddr_p bdaddr,int link_type)347 ng_hci_con_by_bdaddr(ng_hci_unit_p unit, bdaddr_p bdaddr, int link_type)
348 {
349           ng_hci_unit_con_p   con = NULL;
350 
351           LIST_FOREACH(con, &unit->con_list, next)
352                     if (con->link_type == link_type &&
353                         bcmp(&con->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
354                               break;
355 
356           return (con);
357 } /* ng_hci_con_by_bdaddr */
358 
359 /*
360  * Set HCI command timeout
361  * XXX FIXME: check return code from ng_callout
362  */
363 
364 int
ng_hci_command_timeout(ng_hci_unit_p unit)365 ng_hci_command_timeout(ng_hci_unit_p unit)
366 {
367           if (unit->state & NG_HCI_UNIT_COMMAND_PENDING)
368                     panic(
369 "%s: %s - Duplicated command timeout!\n", __func__, NG_NODE_NAME(unit->node));
370 
371           unit->state |= NG_HCI_UNIT_COMMAND_PENDING;
372           ng_callout(&unit->cmd_timo, unit->node, NULL,
373                                         bluetooth_hci_command_timeout(),
374                                         ng_hci_process_command_timeout, NULL, 0);
375 
376           return (0);
377 } /* ng_hci_command_timeout */
378 
379 /*
380  * Unset HCI command timeout
381  */
382 
383 int
ng_hci_command_untimeout(ng_hci_unit_p unit)384 ng_hci_command_untimeout(ng_hci_unit_p unit)
385 {
386           if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
387                     panic(
388 "%s: %s - No command timeout!\n", __func__, NG_NODE_NAME(unit->node));
389 
390           if (ng_uncallout(&unit->cmd_timo, unit->node) == 0)
391                     return (ETIMEDOUT);
392 
393           unit->state &= ~NG_HCI_UNIT_COMMAND_PENDING;
394 
395           return (0);
396 } /* ng_hci_command_untimeout */
397 
398 /*
399  * Set HCI connection timeout
400  * XXX FIXME: check return code from ng_callout
401  */
402 
403 int
ng_hci_con_timeout(ng_hci_unit_con_p con)404 ng_hci_con_timeout(ng_hci_unit_con_p con)
405 {
406           if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
407                     panic(
408 "%s: %s - Duplicated connection timeout!\n",
409                               __func__, NG_NODE_NAME(con->unit->node));
410 
411           con->flags |= NG_HCI_CON_TIMEOUT_PENDING;
412           ng_callout(&con->con_timo, con->unit->node, NULL,
413                                         bluetooth_hci_connect_timeout(),
414                                         ng_hci_process_con_timeout, NULL,
415                                         con->con_handle);
416 
417           return (0);
418 } /* ng_hci_con_timeout */
419 
420 /*
421  * Unset HCI connection timeout
422  */
423 
424 int
ng_hci_con_untimeout(ng_hci_unit_con_p con)425 ng_hci_con_untimeout(ng_hci_unit_con_p con)
426 {
427           if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING))
428                     panic(
429 "%s: %s - No connection timeout!\n", __func__, NG_NODE_NAME(con->unit->node));
430 
431           if (ng_uncallout(&con->con_timo, con->unit->node) == 0)
432                     return (ETIMEDOUT);
433 
434           con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
435 
436           return (0);
437 } /* ng_hci_con_untimeout */
438 
439 #if 0
440 /*
441  * Convert numeric error code/reason to a string
442  */
443 
444 char const * const
445 ng_hci_str_error(u_int16_t code)
446 {
447 #define   LAST_ERROR_CODE                         (NELEM(s)-1)
448           static char const * const     s[] = {
449           /* 0x00 */ "No error",
450           /* 0x01 */ "Unknown HCI command",
451           /* 0x02 */ "No connection",
452           /* 0x03 */ "Hardware failure",
453           /* 0x04 */ "Page timeout",
454           /* 0x05 */ "Authentication failure",
455           /* 0x06 */ "Key missing",
456           /* 0x07 */ "Memory full",
457           /* 0x08 */ "Connection timeout",
458           /* 0x09 */ "Max number of connections",
459           /* 0x0a */ "Max number of SCO connections to a unit",
460           /* 0x0b */ "ACL connection already exists",
461           /* 0x0c */ "Command disallowed",
462           /* 0x0d */ "Host rejected due to limited resources",
463           /* 0x0e */ "Host rejected due to securiity reasons",
464           /* 0x0f */ "Host rejected due to remote unit is a personal unit",
465           /* 0x10 */ "Host timeout",
466           /* 0x11 */ "Unsupported feature or parameter value",
467           /* 0x12 */ "Invalid HCI command parameter",
468           /* 0x13 */ "Other end terminated connection: User ended connection",
469           /* 0x14 */ "Other end terminated connection: Low resources",
470           /* 0x15 */ "Other end terminated connection: About to power off",
471           /* 0x16 */ "Connection terminated by local host",
472           /* 0x17 */ "Repeated attempts",
473           /* 0x18 */ "Pairing not allowed",
474           /* 0x19 */ "Unknown LMP PDU",
475           /* 0x1a */ "Unsupported remote feature",
476           /* 0x1b */ "SCO offset rejected",
477           /* 0x1c */ "SCO interval rejected",
478           /* 0x1d */ "SCO air mode rejected",
479           /* 0x1e */ "Invalid LMP parameters",
480           /* 0x1f */ "Unspecified error",
481           /* 0x20 */ "Unsupported LMP parameter value",
482           /* 0x21 */ "Role change not allowed",
483           /* 0x22 */ "LMP response timeout",
484           /* 0x23 */ "LMP error transaction collision",
485           /* 0x24 */ "LMP PSU not allowed",
486           /* 0x25 */ "Encryption mode not acceptable",
487           /* 0x26 */ "Unit key used",
488           /* 0x27 */ "QoS is not supported",
489           /* 0x28 */ "Instant passed",
490           /* 0x29 */ "Paring with unit key not supported",
491           /* SHOULD ALWAYS BE LAST */ "Unknown error"
492           };
493 
494           return ((code >= LAST_ERROR_CODE)? s[LAST_ERROR_CODE] : s[code]);
495 } /* ng_hci_str_error */
496 #endif
497 
498