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