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
57static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p);
58static 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
64int
65ng_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
108static int
109ng_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_NOWAIT, 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);
297out:
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
308static int
309ng_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 - Invalid 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_NOWAIT, 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);
466out:
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
476int
477ng_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_NOWAIT, 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);
557out:
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
567int
568ng_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
635int
636ng_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
681int
682ng_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_NOWAIT, 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	}
865out:
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
875int
876ng_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
933int
934ng_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_NOWAIT, 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);
1036out:
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
1046int
1047ng_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
1101int
1102ng_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
1151void
1152ng_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
1213