]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/netgraph/bluetooth/hci/ng_hci_ulpi.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / netgraph / bluetooth / hci / ng_hci_ulpi.c
1 /*
2  * ng_hci_ulpi.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_ulpi.c,v 1.7 2003/09/08 18:57:51 max Exp $
31  * $FreeBSD$
32  */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/endian.h>
38 #include <sys/malloc.h>
39 #include <sys/mbuf.h>
40 #include <sys/queue.h>
41 #include <netgraph/ng_message.h>
42 #include <netgraph/netgraph.h>
43 #include <netgraph/bluetooth/include/ng_bluetooth.h>
44 #include <netgraph/bluetooth/include/ng_hci.h>
45 #include <netgraph/bluetooth/hci/ng_hci_var.h>
46 #include <netgraph/bluetooth/hci/ng_hci_cmds.h>
47 #include <netgraph/bluetooth/hci/ng_hci_evnt.h>
48 #include <netgraph/bluetooth/hci/ng_hci_ulpi.h>
49 #include <netgraph/bluetooth/hci/ng_hci_misc.h>
50
51 /******************************************************************************
52  ******************************************************************************
53  **                 Upper Layer Protocol Interface module
54  ******************************************************************************
55  ******************************************************************************/
56
57 static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p);
58 static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p);
59
60 /*
61  * Process LP_ConnectReq event from the upper layer protocol
62  */
63
64 int
65 ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
66 {
67         if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
68                 NG_HCI_WARN(
69 "%s: %s - unit is not ready, state=%#x\n",
70                         __func__, NG_NODE_NAME(unit->node), unit->state);
71
72                 NG_FREE_ITEM(item);
73
74                 return (ENXIO);
75         }
76
77         if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) {
78                 NG_HCI_ALERT(
79 "%s: %s - invalid LP_ConnectReq message size=%d\n",
80                         __func__, NG_NODE_NAME(unit->node),
81                         NGI_MSG(item)->header.arglen);
82
83                 NG_FREE_ITEM(item);
84
85                 return (EMSGSIZE);
86         }
87
88         if (((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type == NG_HCI_LINK_ACL)
89                 return (ng_hci_lp_acl_con_req(unit, item, hook));
90
91         if (hook != unit->sco) {
92                 NG_HCI_WARN(
93 "%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n",
94                         __func__, NG_NODE_NAME(unit->node), hook);
95
96                 NG_FREE_ITEM(item);
97
98                 return (EINVAL);
99         }
100
101         return (ng_hci_lp_sco_con_req(unit, item, hook));
102 } /* ng_hci_lp_con_req */
103
104 /*
105  * Request to create new ACL connection
106  */
107
108 static int
109 ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
110 {
111         struct acl_con_req {
112                 ng_hci_cmd_pkt_t         hdr;
113                 ng_hci_create_con_cp     cp;
114         } __attribute__ ((packed))      *req = NULL;
115         ng_hci_lp_con_req_ep            *ep = NULL;
116         ng_hci_unit_con_p                con = NULL;
117         ng_hci_neighbor_t               *n = NULL;
118         struct mbuf                     *m = NULL;
119         int                              error = 0;
120
121         ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
122
123         /*
124          * Only one ACL connection can exist between each pair of units.
125          * So try to find ACL connection descriptor (in any state) that
126          * has requested remote BD_ADDR.
127          *
128          * Two cases:
129          *
130          * 1) We do not have connection to the remote unit. This is simple.
131          *    Just create new connection descriptor and send HCI command to
132          *    create new connection.
133          *
134          * 2) We do have connection descriptor. We need to check connection
135          *    state:
136          * 
137          * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
138          *      accepting connection from the remote unit. This is a race
139          *      condition. We will ignore this message.
140          *
141          * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
142          *      requested connection or we just accepted it. In any case
143          *      all we need to do here is set appropriate notification bit
144          *      and wait.
145          *      
146          * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back
147          *      and let upper layer know that we have connection already.
148          */
149
150         con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
151         if (con != NULL) {
152                 switch (con->state) {
153                 case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
154                         error = EALREADY;
155                         break;
156
157                 case NG_HCI_CON_W4_CONN_COMPLETE:
158                         if (hook == unit->acl)
159                                 con->flags |= NG_HCI_CON_NOTIFY_ACL;
160                         else
161                                 con->flags |= NG_HCI_CON_NOTIFY_SCO;
162                         break;
163
164                 case NG_HCI_CON_OPEN: {
165                         struct ng_mesg          *msg = NULL;
166                         ng_hci_lp_con_cfm_ep    *cfm = NULL;
167
168                         if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
169                                 NGI_GET_MSG(item, msg);
170                                 NG_FREE_MSG(msg);
171
172                                 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 
173                                         NGM_HCI_LP_CON_CFM, sizeof(*cfm), 
174                                         M_NOWAIT);
175                                 if (msg != NULL) {
176                                         cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
177                                         cfm->status = 0;
178                                         cfm->link_type = con->link_type;
179                                         cfm->con_handle = con->con_handle;
180                                         bcopy(&con->bdaddr, &cfm->bdaddr, 
181                                                 sizeof(cfm->bdaddr));
182
183                                         /*
184                                          * This will forward item back to
185                                          * sender and set item to NULL
186                                          */
187
188                                         _NGI_MSG(item) = msg;
189                                         NG_FWD_ITEM_HOOK(error, item, hook);
190                                 } else
191                                         error = ENOMEM;
192                         } else
193                                 NG_HCI_INFO(
194 "%s: %s - Source hook is not valid, hook=%p\n",
195                                         __func__, NG_NODE_NAME(unit->node), 
196                                         hook);
197                         } break;
198
199                 default:
200                         panic(
201 "%s: %s - Invalid connection state=%d\n",
202                                 __func__, NG_NODE_NAME(unit->node), con->state);
203                         break;
204                 }
205
206                 goto out;
207         }
208
209         /*
210          * If we got here then we need to create new ACL connection descriptor
211          * and submit HCI command. First create new connection desriptor, set
212          * bdaddr and notification flags.
213          */
214
215         con = ng_hci_new_con(unit, NG_HCI_LINK_ACL);
216         if (con == NULL) {
217                 error = ENOMEM;
218                 goto out;
219         }
220
221         bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
222
223         /* 
224          * Create HCI command 
225          */
226
227         MGETHDR(m, M_DONTWAIT, MT_DATA);
228         if (m == NULL) {
229                 ng_hci_free_con(con);
230                 error = ENOBUFS;
231                 goto out;
232         }
233
234         m->m_pkthdr.len = m->m_len = sizeof(*req);
235         req = mtod(m, struct acl_con_req *);
236         req->hdr.type = NG_HCI_CMD_PKT;
237         req->hdr.length = sizeof(req->cp);
238         req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
239                                         NG_HCI_OCF_CREATE_CON));
240
241         bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr));
242
243         req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
244         if (unit->features[0] & NG_HCI_LMP_3SLOT)
245                 req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3);
246         if (unit->features[0] & NG_HCI_LMP_5SLOT)
247                 req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5);
248
249         req->cp.pkt_type &= unit->packet_mask;
250         if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1|
251                                  NG_HCI_PKT_DM3|NG_HCI_PKT_DH3|
252                                  NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0)
253                 req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
254
255         req->cp.pkt_type = htole16(req->cp.pkt_type);
256
257         if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch)
258                 req->cp.accept_role_switch = 1;
259         else
260                 req->cp.accept_role_switch = 0;
261
262         /*
263          * We may speed up connect by specifying valid parameters. 
264          * So check the neighbor cache.
265          */
266
267         n = ng_hci_get_neighbor(unit, &ep->bdaddr);
268         if (n == NULL) {
269                 req->cp.page_scan_rep_mode = 0;
270                 req->cp.page_scan_mode = 0;
271                 req->cp.clock_offset = 0;
272         } else {
273                 req->cp.page_scan_rep_mode = n->page_scan_rep_mode;
274                 req->cp.page_scan_mode = n->page_scan_mode;
275                 req->cp.clock_offset = htole16(n->clock_offset);
276         }
277
278         /* 
279          * Adust connection state 
280          */
281
282         if (hook == unit->acl)
283                 con->flags |= NG_HCI_CON_NOTIFY_ACL;
284         else
285                 con->flags |= NG_HCI_CON_NOTIFY_SCO;
286
287         con->state = NG_HCI_CON_W4_CONN_COMPLETE;
288         ng_hci_con_timeout(con);
289
290         /* 
291          * Queue and send HCI command 
292          */
293
294         NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
295         if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
296                 error = ng_hci_send_command(unit);
297 out:
298         if (item != NULL)
299                 NG_FREE_ITEM(item);
300
301         return (error);
302 } /* ng_hci_lp_acl_con_req */
303
304 /*
305  * Request to create new SCO connection
306  */
307
308 static int
309 ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
310 {
311         struct sco_con_req {
312                 ng_hci_cmd_pkt_t         hdr;
313                 ng_hci_add_sco_con_cp    cp;
314         } __attribute__ ((packed))      *req = NULL;
315         ng_hci_lp_con_req_ep            *ep = NULL;
316         ng_hci_unit_con_p                acl_con = NULL, sco_con = NULL;
317         struct mbuf                     *m = NULL;
318         int                              error = 0;
319
320         ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
321
322         /*
323          * SCO connection without ACL link
324          *
325          * If upper layer requests SCO connection and there is no open ACL 
326          * connection to the desired remote unit, we will reject the request.
327          */
328
329         LIST_FOREACH(acl_con, &unit->con_list, next)
330                 if (acl_con->link_type == NG_HCI_LINK_ACL &&
331                     acl_con->state == NG_HCI_CON_OPEN &&
332                     bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
333                         break;
334
335         if (acl_con == NULL) {
336                 NG_HCI_INFO(
337 "%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n",
338                         __func__, NG_NODE_NAME(unit->node),
339                         ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
340                         ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
341
342                 error = ENOENT;
343                 goto out;
344         }
345
346         /*
347          * Multiple SCO connections can exist between the same pair of units.
348          * We assume that multiple SCO connections have to be opened one after 
349          * another. 
350          *
351          * Try to find SCO connection descriptor that matches the following:
352          *
353          * 1) sco_con->link_type == NG_HCI_LINK_SCO
354          * 
355          * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
356          *    sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE
357          * 
358          * 3) sco_con->bdaddr == ep->bdaddr
359          *
360          * Two cases:
361          *
362          * 1) We do not have connection descriptor. This is simple. Just 
363          *    create new connection and submit Add_SCO_Connection command.
364          *
365          * 2) We do have connection descriptor. We need to check the state.
366          *
367          * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting
368          *      connection from the remote unit. This is a race condition and
369          *      we will ignore the request.
370          *
371          * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested
372          *      connection or we just accepted it.
373          */
374
375         LIST_FOREACH(sco_con, &unit->con_list, next)
376                 if (sco_con->link_type == NG_HCI_LINK_SCO &&
377                     (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
378                      sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
379                     bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
380                         break;
381
382         if (sco_con != NULL) {
383                 switch (sco_con->state) {
384                 case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
385                         error = EALREADY;
386                         break;
387
388                 case NG_HCI_CON_W4_CONN_COMPLETE:
389                         sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
390                         break;
391
392                 default:
393                         panic(
394 "%s: %s - Inavalid connection state=%d\n",
395                                 __func__, NG_NODE_NAME(unit->node),
396                                 sco_con->state);
397                         break;
398                 }
399
400                 goto out;
401         }
402
403         /*
404          * If we got here then we need to create new SCO connection descriptor
405          * and submit HCI command.
406          */
407
408         sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO);
409         if (sco_con == NULL) {
410                 error = ENOMEM;
411                 goto out;
412         }
413
414         bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr));
415
416         /* 
417          * Create HCI command 
418          */
419
420         MGETHDR(m, M_DONTWAIT, MT_DATA);
421         if (m == NULL) {
422                 ng_hci_free_con(sco_con);
423                 error = ENOBUFS;
424                 goto out;
425         }
426
427         m->m_pkthdr.len = m->m_len = sizeof(*req);
428         req = mtod(m, struct sco_con_req *);
429         req->hdr.type = NG_HCI_CMD_PKT;
430         req->hdr.length = sizeof(req->cp);
431         req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
432                                         NG_HCI_OCF_ADD_SCO_CON));
433
434         req->cp.con_handle = htole16(acl_con->con_handle);
435
436         req->cp.pkt_type = NG_HCI_PKT_HV1;
437         if (unit->features[1] & NG_HCI_LMP_HV2_PKT)
438                 req->cp.pkt_type |= NG_HCI_PKT_HV2;
439         if (unit->features[1] & NG_HCI_LMP_HV3_PKT)
440                 req->cp.pkt_type |= NG_HCI_PKT_HV3;
441
442         req->cp.pkt_type &= unit->packet_mask;
443         if ((req->cp.pkt_type & (NG_HCI_PKT_HV1|
444                                  NG_HCI_PKT_HV2|
445                                  NG_HCI_PKT_HV3)) == 0)
446                 req->cp.pkt_type = NG_HCI_PKT_HV1;
447
448         req->cp.pkt_type = htole16(req->cp.pkt_type);
449
450         /* 
451          * Adust connection state
452          */
453
454         sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
455
456         sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE;
457         ng_hci_con_timeout(sco_con);
458
459         /* 
460          * Queue and send HCI command
461          */
462
463         NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
464         if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
465                 error = ng_hci_send_command(unit);
466 out:
467         NG_FREE_ITEM(item);
468
469         return (error);
470 } /* ng_hci_lp_sco_con_req */
471
472 /*
473  * Process LP_DisconnectReq event from the upper layer protocol
474  */
475
476 int
477 ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook)
478 {
479         struct discon_req {
480                 ng_hci_cmd_pkt_t         hdr;
481                 ng_hci_discon_cp         cp;
482         } __attribute__ ((packed))      *req = NULL;
483         ng_hci_lp_discon_req_ep         *ep = NULL;
484         ng_hci_unit_con_p                con = NULL;
485         struct mbuf                     *m = NULL;
486         int                              error = 0;
487
488         /* Check if unit is ready */
489         if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
490                 NG_HCI_WARN(
491 "%s: %s - unit is not ready, state=%#x\n",
492                         __func__, NG_NODE_NAME(unit->node), unit->state);
493
494                 error = ENXIO;
495                 goto out;
496         }
497
498         if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
499                 NG_HCI_ALERT(
500 "%s: %s - invalid LP_DisconnectReq message size=%d\n",
501                         __func__, NG_NODE_NAME(unit->node),
502                         NGI_MSG(item)->header.arglen);
503
504                 error = EMSGSIZE;
505                 goto out;
506         }
507
508         ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data);
509
510         con = ng_hci_con_by_handle(unit, ep->con_handle);
511         if (con == NULL) {
512                 NG_HCI_ERR(
513 "%s: %s - invalid connection handle=%d\n",
514                         __func__, NG_NODE_NAME(unit->node), ep->con_handle);
515
516                 error = ENOENT;
517                 goto out;
518         }
519
520         if (con->state != NG_HCI_CON_OPEN) {
521                 NG_HCI_ERR(
522 "%s: %s - invalid connection state=%d, handle=%d\n",
523                         __func__, NG_NODE_NAME(unit->node), con->state,
524                         ep->con_handle);
525
526                 error = EINVAL;
527                 goto out;
528         }
529
530         /* 
531          * Create HCI command
532          */
533
534         MGETHDR(m, M_DONTWAIT, MT_DATA);
535         if (m == NULL) {
536                 error = ENOBUFS;
537                 goto out;
538         }
539
540         m->m_pkthdr.len = m->m_len = sizeof(*req);
541         req = mtod(m, struct discon_req *);
542         req->hdr.type = NG_HCI_CMD_PKT;
543         req->hdr.length = sizeof(req->cp);
544         req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
545                                                         NG_HCI_OCF_DISCON));
546
547         req->cp.con_handle = htole16(ep->con_handle);
548         req->cp.reason = ep->reason;
549
550         /* 
551          * Queue and send HCI command 
552          */
553
554         NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
555         if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
556                 error = ng_hci_send_command(unit);
557 out:
558         NG_FREE_ITEM(item);
559
560         return (error);
561 } /* ng_hci_lp_discon_req */
562
563 /*
564  * Send LP_ConnectCfm event to the upper layer protocol
565  */
566
567 int
568 ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status)
569 {
570         ng_hci_unit_p            unit = con->unit;
571         struct ng_mesg          *msg = NULL;
572         ng_hci_lp_con_cfm_ep    *ep = NULL;
573         int                      error;
574
575         /*
576          * Check who wants to be notified. For ACL links both ACL and SCO
577          * upstream hooks will be notified (if required). For SCO links
578          * only SCO upstream hook will receive notification
579          */
580
581         if (con->link_type == NG_HCI_LINK_ACL && 
582             con->flags & NG_HCI_CON_NOTIFY_ACL) {
583                 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
584                         NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, 
585                                 sizeof(*ep), M_NOWAIT);
586                         if (msg != NULL) {
587                                 ep = (ng_hci_lp_con_cfm_ep *) msg->data;
588                                 ep->status = status;
589                                 ep->link_type = con->link_type;
590                                 ep->con_handle = con->con_handle;
591                                 bcopy(&con->bdaddr, &ep->bdaddr, 
592                                         sizeof(ep->bdaddr));
593
594                                 NG_SEND_MSG_HOOK(error, unit->node, msg,
595                                         unit->acl, 0);
596                         }
597                 } else
598                         NG_HCI_INFO(
599 "%s: %s - ACL hook not valid, hook=%p\n",
600                                 __func__, NG_NODE_NAME(unit->node), unit->acl);
601
602                 con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
603         }
604
605         if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
606                 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
607                         NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, 
608                                 sizeof(*ep), M_NOWAIT);
609                         if (msg != NULL) {
610                                 ep = (ng_hci_lp_con_cfm_ep *) msg->data;
611                                 ep->status = status;
612                                 ep->link_type = con->link_type;
613                                 ep->con_handle = con->con_handle;
614                                 bcopy(&con->bdaddr, &ep->bdaddr, 
615                                         sizeof(ep->bdaddr));
616
617                                 NG_SEND_MSG_HOOK(error, unit->node, msg,
618                                         unit->sco, 0);
619                         }
620                 } else
621                         NG_HCI_INFO(
622 "%s: %s - SCO hook not valid, hook=%p\n",
623                                 __func__, NG_NODE_NAME(unit->node), unit->acl);
624
625                 con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
626         }
627
628         return (0);
629 } /* ng_hci_lp_con_cfm */
630
631 /*
632  * Send LP_ConnectInd event to the upper layer protocol
633  */
634
635 int
636 ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass)
637 {
638         ng_hci_unit_p            unit = con->unit;
639         struct ng_mesg          *msg = NULL;
640         ng_hci_lp_con_ind_ep    *ep = NULL;
641         hook_p                   hook = NULL;
642         int                      error = 0;
643
644         /*
645          * Connection_Request event is generated for specific link type.
646          * Use link_type to select upstream hook.
647          */
648
649         if (con->link_type == NG_HCI_LINK_ACL)
650                 hook = unit->acl;
651         else
652                 hook = unit->sco;
653
654         if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
655                 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND, 
656                         sizeof(*ep), M_NOWAIT);
657                 if (msg == NULL)
658                         return (ENOMEM);
659
660                 ep = (ng_hci_lp_con_ind_ep *)(msg->data);
661                 ep->link_type = con->link_type;
662                 bcopy(uclass, ep->uclass, sizeof(ep->uclass));
663                 bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
664
665                 NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0);
666         } else {
667                 NG_HCI_WARN(
668 "%s: %s - Upstream hook is not connected or not valid, hook=%p\n",
669                         __func__, NG_NODE_NAME(unit->node), hook);
670
671                 error = ENOTCONN;
672         }
673
674         return (error);
675 } /* ng_hci_lp_con_ind */
676
677 /*
678  * Process LP_ConnectRsp event from the upper layer protocol
679  */
680
681 int
682 ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook)
683 {
684         struct con_rsp_req {
685                 ng_hci_cmd_pkt_t                 hdr;
686                 union {
687                         ng_hci_accept_con_cp     acc;
688                         ng_hci_reject_con_cp     rej;
689                 } __attribute__ ((packed))       cp;
690         } __attribute__ ((packed))              *req = NULL;
691         ng_hci_lp_con_rsp_ep                    *ep = NULL;
692         ng_hci_unit_con_p                        con = NULL;
693         struct mbuf                             *m = NULL;
694         int                                      error = 0;
695
696         /* Check if unit is ready */
697         if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
698                 NG_HCI_WARN(
699 "%s: %s - unit is not ready, state=%#x\n",
700                         __func__, NG_NODE_NAME(unit->node), unit->state);
701
702                 error = ENXIO;
703                 goto out;
704         }
705
706         if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
707                 NG_HCI_ALERT(
708 "%s: %s - invalid LP_ConnectRsp message size=%d\n",
709                         __func__, NG_NODE_NAME(unit->node),
710                         NGI_MSG(item)->header.arglen);
711
712                 error = EMSGSIZE;
713                 goto out;
714         }
715
716         ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data);
717
718         /*
719          * Here we have to deal with race. Upper layers might send conflicting
720          * requests. One might send Accept and other Reject. We will not try
721          * to solve all the problems, so first request will always win.
722          *
723          * Try to find connection that matches the following:
724          *
725          * 1) con->link_type == ep->link_type
726          *
727          * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
728          *    con->state == NG_HCI_CON_W4_CONN_COMPLETE
729          *
730          * 3) con->bdaddr == ep->bdaddr
731          *
732          * Two cases:
733          *
734          * 1) We do not have connection descriptor. Could be bogus request or
735          *    we have rejected connection already.
736          *
737          * 2) We do have connection descriptor. Then we need to check state:
738          *
739          * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested 
740          *      connection and it is a first response from the upper layer.
741          *      if "status == 0" (Accept) then we will send Accept_Connection
742          *      command and change connection state to W4_CONN_COMPLETE, else
743          *      send reject and delete connection.
744          *
745          * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted 
746          *      connection. If "status == 0" we just need to link request
747          *      and wait, else ignore Reject request.
748          */
749
750         LIST_FOREACH(con, &unit->con_list, next)
751                 if (con->link_type == ep->link_type &&
752                     (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
753                      con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
754                     bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
755                         break;
756
757         if (con == NULL) {
758                 /* Reject for non-existing connection is fine */
759                 error = (ep->status == 0)? ENOENT : 0;
760                 goto out;
761         }
762
763         /* 
764          * Remove connection timeout and check connection state.
765          * Note: if ng_hci_con_untimeout() fails (returns non-zero value) then
766          * timeout already happened and event went into node's queue.
767          */
768
769         if ((error = ng_hci_con_untimeout(con)) != 0)
770                 goto out;
771
772         switch (con->state) {
773         case NG_HCI_CON_W4_LP_CON_RSP:
774
775                 /* 
776                  * Create HCI command 
777                  */
778
779                 MGETHDR(m, M_DONTWAIT, MT_DATA);
780                 if (m == NULL) {
781                         error = ENOBUFS;
782                         goto out;
783                 }
784                 
785                 req = mtod(m, struct con_rsp_req *);
786                 req->hdr.type = NG_HCI_CMD_PKT;
787
788                 if (ep->status == 0) {
789                         req->hdr.length = sizeof(req->cp.acc);
790                         req->hdr.opcode = htole16(NG_HCI_OPCODE(
791                                                         NG_HCI_OGF_LINK_CONTROL,
792                                                         NG_HCI_OCF_ACCEPT_CON));
793
794                         bcopy(&ep->bdaddr, &req->cp.acc.bdaddr,
795                                 sizeof(req->cp.acc.bdaddr));
796
797                         /*
798                          * We are accepting connection, so if we support role 
799                          * switch and role switch was enabled then set role to 
800                          * NG_HCI_ROLE_MASTER and let LM peform role switch.
801                          * Otherwise we remain slave. In this case LM WILL NOT
802                          * perform role switch.
803                          */
804
805                         if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&
806                             unit->role_switch)
807                                 req->cp.acc.role = NG_HCI_ROLE_MASTER;
808                         else
809                                 req->cp.acc.role = NG_HCI_ROLE_SLAVE;
810
811                         /* 
812                          * Adjust connection state 
813                          */
814
815                         if (hook == unit->acl)
816                                 con->flags |= NG_HCI_CON_NOTIFY_ACL;
817                         else
818                                 con->flags |= NG_HCI_CON_NOTIFY_SCO;
819
820                         con->state = NG_HCI_CON_W4_CONN_COMPLETE;
821                         ng_hci_con_timeout(con);
822                 } else {
823                         req->hdr.length = sizeof(req->cp.rej);
824                         req->hdr.opcode = htole16(NG_HCI_OPCODE(
825                                                         NG_HCI_OGF_LINK_CONTROL,
826                                                         NG_HCI_OCF_REJECT_CON));
827
828                         bcopy(&ep->bdaddr, &req->cp.rej.bdaddr,
829                                 sizeof(req->cp.rej.bdaddr));
830
831                         req->cp.rej.reason = ep->status;
832
833                         /*
834                          * Free connection descritor
835                          * Item will be deleted just before return.
836                          */
837                         
838                         ng_hci_free_con(con);
839                 }
840
841                 m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length;
842
843                 /* Queue and send HCI command */
844                 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
845                 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
846                         error = ng_hci_send_command(unit);
847                 break;
848
849         case NG_HCI_CON_W4_CONN_COMPLETE:
850                 if (ep->status == 0) {
851                         if (hook == unit->acl)
852                                 con->flags |= NG_HCI_CON_NOTIFY_ACL;
853                         else
854                                 con->flags |= NG_HCI_CON_NOTIFY_SCO;
855                 } else
856                         error = EPERM;
857                 break;
858
859         default:
860                 panic(
861 "%s: %s - Invalid connection state=%d\n",
862                         __func__, NG_NODE_NAME(unit->node), con->state);
863                 break;
864         }
865 out:
866         NG_FREE_ITEM(item);
867
868         return (error);
869 } /* ng_hci_lp_con_rsp */
870
871 /*
872  * Send LP_DisconnectInd to the upper layer protocol
873  */
874
875 int
876 ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason)
877 {
878         ng_hci_unit_p            unit = con->unit;
879         struct ng_mesg          *msg = NULL;
880         ng_hci_lp_discon_ind_ep *ep = NULL;
881         int                      error = 0;
882
883         /*
884          * Disconnect_Complete event is generated for specific connection
885          * handle. For ACL connection handles both ACL and SCO upstream
886          * hooks will receive notification. For SCO connection handles
887          * only SCO upstream hook will receive notification.
888          */
889
890         if (con->link_type == NG_HCI_LINK_ACL) {
891                 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
892                         NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 
893                                 NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT);
894                         if (msg == NULL)
895                                 return (ENOMEM);
896
897                         ep = (ng_hci_lp_discon_ind_ep *) msg->data;
898                         ep->reason = reason;
899                         ep->link_type = con->link_type;
900                         ep->con_handle = con->con_handle;
901
902                         NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,0);
903                 } else
904                         NG_HCI_INFO(
905 "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
906                                 __func__, NG_NODE_NAME(unit->node), unit->acl);
907         }
908
909         if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
910                 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND, 
911                         sizeof(*ep), M_NOWAIT);
912                 if (msg == NULL)
913                         return (ENOMEM);
914
915                 ep = (ng_hci_lp_discon_ind_ep *) msg->data;
916                 ep->reason = reason;
917                 ep->link_type = con->link_type;
918                 ep->con_handle = con->con_handle;
919
920                 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);
921         } else
922                 NG_HCI_INFO(
923 "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
924                         __func__, NG_NODE_NAME(unit->node), unit->sco);
925
926         return (0);
927 } /* ng_hci_lp_discon_ind */
928
929 /*
930  * Process LP_QoSReq action from the upper layer protocol
931  */
932
933 int
934 ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook)
935 {
936         struct qos_setup_req {
937                 ng_hci_cmd_pkt_t         hdr;
938                 ng_hci_qos_setup_cp      cp;
939         } __attribute__ ((packed))      *req = NULL;
940         ng_hci_lp_qos_req_ep            *ep = NULL;
941         ng_hci_unit_con_p                con = NULL;
942         struct mbuf                     *m = NULL;
943         int                              error = 0;
944
945         /* Check if unit is ready */
946         if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
947                 NG_HCI_WARN(
948 "%s: %s - unit is not ready, state=%#x\n",
949                         __func__, NG_NODE_NAME(unit->node), unit->state);
950
951                 error = ENXIO;
952                 goto out;
953         }
954
955         if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
956                 NG_HCI_ALERT(
957 "%s: %s - invalid LP_QoSSetupReq message size=%d\n",
958                         __func__, NG_NODE_NAME(unit->node),
959                         NGI_MSG(item)->header.arglen);
960
961                 error = EMSGSIZE;
962                 goto out;
963         }
964
965         ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data);
966
967         con = ng_hci_con_by_handle(unit, ep->con_handle);
968         if (con == NULL) {
969                 NG_HCI_ERR(
970 "%s: %s - invalid connection handle=%d\n",
971                         __func__, NG_NODE_NAME(unit->node), ep->con_handle);
972
973                 error = EINVAL;
974                 goto out;
975         }
976
977         if (con->link_type != NG_HCI_LINK_ACL) {
978                 NG_HCI_ERR("%s: %s - invalid link type=%d\n",
979                         __func__, NG_NODE_NAME(unit->node), con->link_type);
980
981                 error = EINVAL;
982                 goto out;
983         }
984
985         if (con->state != NG_HCI_CON_OPEN) {
986                 NG_HCI_ERR(
987 "%s: %s - invalid connection state=%d, handle=%d\n",
988                         __func__, NG_NODE_NAME(unit->node), con->state,
989                         con->con_handle);
990
991                 error = EINVAL;
992                 goto out;
993         }
994
995         /* 
996          * Create HCI command 
997          */
998
999         MGETHDR(m, M_DONTWAIT, MT_DATA);
1000         if (m == NULL) {
1001                 error = ENOBUFS;
1002                 goto out;
1003         }
1004
1005         m->m_pkthdr.len = m->m_len = sizeof(*req);
1006         req = mtod(m, struct qos_setup_req *);
1007         req->hdr.type = NG_HCI_CMD_PKT;
1008         req->hdr.length = sizeof(req->cp);
1009         req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
1010                         NG_HCI_OCF_QOS_SETUP));
1011
1012         req->cp.con_handle = htole16(ep->con_handle);
1013         req->cp.flags = ep->flags;
1014         req->cp.service_type = ep->service_type;
1015         req->cp.token_rate = htole32(ep->token_rate);
1016         req->cp.peak_bandwidth = htole32(ep->peak_bandwidth);
1017         req->cp.latency = htole32(ep->latency);
1018         req->cp.delay_variation = htole32(ep->delay_variation);
1019
1020         /* 
1021          * Adjust connection state 
1022          */
1023
1024         if (hook == unit->acl)
1025                 con->flags |= NG_HCI_CON_NOTIFY_ACL;
1026         else
1027                 con->flags |= NG_HCI_CON_NOTIFY_SCO;
1028
1029         /* 
1030          * Queue and send HCI command 
1031          */
1032
1033         NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1034         if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1035                 error = ng_hci_send_command(unit);
1036 out:
1037         NG_FREE_ITEM(item);
1038         
1039         return (error);
1040 } /* ng_hci_lp_qos_req */
1041
1042 /*
1043  * Send LP_QoSCfm event to the upper layer protocol
1044  */
1045
1046 int
1047 ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status)
1048 {
1049         ng_hci_unit_p            unit = con->unit;
1050         struct ng_mesg          *msg = NULL;
1051         ng_hci_lp_qos_cfm_ep    *ep = NULL;
1052         int                      error;
1053
1054         if (con->flags & NG_HCI_CON_NOTIFY_ACL) {
1055                 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1056                         NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, 
1057                                 sizeof(*ep), M_NOWAIT);
1058                         if (msg != NULL) {
1059                                 ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1060                                 ep->status = status;
1061                                 ep->con_handle = con->con_handle;
1062
1063                                 NG_SEND_MSG_HOOK(error, unit->node, msg,
1064                                         unit->acl, 0);
1065                         }
1066                 } else
1067                         NG_HCI_INFO(
1068 "%s: %s - ACL hook not valid, hook=%p\n",
1069                                 __func__, NG_NODE_NAME(unit->node), unit->acl);
1070
1071                 con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
1072         }
1073
1074         if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
1075                 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1076                         NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, 
1077                                 sizeof(*ep), M_NOWAIT);
1078                         if (msg != NULL) {
1079                                 ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1080                                 ep->status = status;
1081                                 ep->con_handle = con->con_handle;
1082
1083                                 NG_SEND_MSG_HOOK(error, unit->node, msg,
1084                                         unit->sco, 0);
1085                         }
1086                 } else
1087                         NG_HCI_INFO(
1088 "%s: %s - SCO hook not valid, hook=%p\n",
1089                                  __func__, NG_NODE_NAME(unit->node), unit->sco);
1090
1091                 con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
1092         }
1093         
1094         return (0);
1095 } /* ng_hci_lp_qos_cfm */
1096
1097 /*
1098  * Send LP_QoSViolationInd event to the upper layer protocol
1099  */
1100
1101 int
1102 ng_hci_lp_qos_ind(ng_hci_unit_con_p con)
1103 {
1104         ng_hci_unit_p            unit = con->unit;
1105         struct ng_mesg          *msg = NULL;
1106         ng_hci_lp_qos_ind_ep    *ep = NULL;
1107         int                      error;
1108
1109         /* 
1110          * QoS Violation can only be generated for ACL connection handles.
1111          * Both ACL and SCO upstream hooks will receive notification.
1112          */
1113
1114         if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1115                 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, 
1116                         sizeof(*ep), M_NOWAIT);
1117                 if (msg == NULL)
1118                         return (ENOMEM);
1119
1120                 ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1121                 ep->con_handle = con->con_handle;
1122
1123                 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0);
1124         } else
1125                 NG_HCI_INFO(
1126 "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
1127                         __func__, NG_NODE_NAME(unit->node), unit->acl);
1128
1129         if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1130                 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, 
1131                         sizeof(*ep), M_NOWAIT);
1132                 if (msg == NULL)
1133                         return (ENOMEM);
1134
1135                 ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1136                 ep->con_handle = con->con_handle;
1137
1138                 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);
1139         } else
1140                 NG_HCI_INFO(
1141 "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
1142                         __func__, NG_NODE_NAME(unit->node), unit->sco);
1143
1144         return (0);
1145 } /* ng_hci_lp_qos_ind */
1146
1147 /*
1148  * Process connection timeout
1149  */
1150
1151 void
1152 ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
1153 {
1154         ng_hci_unit_p           unit = NULL;
1155         ng_hci_unit_con_p       con = NULL;
1156
1157         if (NG_NODE_NOT_VALID(node)) {
1158                 printf("%s: Netgraph node is not valid\n", __func__);
1159                 return;
1160         }
1161
1162         unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
1163         con = ng_hci_con_by_handle(unit, con_handle);
1164
1165         if (con == NULL) {
1166                 NG_HCI_ALERT(
1167 "%s: %s - could not find connection, handle=%d\n",
1168                         __func__, NG_NODE_NAME(node), con_handle);
1169                 return;
1170         }
1171
1172         if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) {
1173                 NG_HCI_ALERT(
1174 "%s: %s - no pending connection timeout, handle=%d, state=%d, flags=%#x\n",
1175                         __func__, NG_NODE_NAME(node), con_handle, con->state,
1176                         con->flags);
1177                 return;
1178         }
1179
1180         con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
1181
1182         /*
1183          * We expect to receive connection timeout in one of the following
1184          * states:
1185          *
1186          * 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded
1187          *    to our LP_CON_IND. Do nothing and destroy connection. Remote peer
1188          *    most likely already gave up on us.
1189          * 
1190          * 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection
1191          *    (or we in the process of accepting it) and baseband has timedout
1192          *    on us. Inform upper layers and send LP_CON_CFM.
1193          */
1194
1195         switch (con->state) {
1196         case NG_HCI_CON_W4_LP_CON_RSP:
1197                 break;
1198
1199         case NG_HCI_CON_W4_CONN_COMPLETE:
1200                 ng_hci_lp_con_cfm(con, 0xee);
1201                 break;
1202
1203         default:
1204                 panic(
1205 "%s: %s - Invalid connection state=%d\n",
1206                         __func__, NG_NODE_NAME(node), con->state);
1207                 break;
1208         }
1209
1210         ng_hci_free_con(con);
1211 } /* ng_hci_process_con_timeout */
1212