1 /*
2  * ng_hci_cmds.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_cmds.c,v 1.4 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  **                     HCI commands processing module
54  ******************************************************************************
55  ******************************************************************************/
56 
57 #undef	min
58 #define	min(a, b)	((a) < (b))? (a) : (b)
59 
60 static int  complete_command (ng_hci_unit_p, int, struct mbuf **);
61 
62 static int process_link_control_params
63 	(ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
64 static int process_link_policy_params
65 	(ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
66 static int process_hc_baseband_params
67 	(ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
68 static int process_info_params
69 	(ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
70 static int process_status_params
71 	(ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
72 static int process_testing_params
73 	(ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
74 static int process_le_params
75 	(ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
76 
77 static int process_link_control_status
78 	(ng_hci_unit_p, ng_hci_command_status_ep *, struct mbuf *);
79 static int process_link_policy_status
80 	(ng_hci_unit_p, ng_hci_command_status_ep *, struct mbuf *);
81 static int process_le_status
82 	(ng_hci_unit_p, ng_hci_command_status_ep *, struct mbuf *);
83 
84 /*
85  * Send HCI command to the driver.
86  */
87 
88 int
89 ng_hci_send_command(ng_hci_unit_p unit)
90 {
91 	struct mbuf	*m0 = NULL, *m = NULL;
92 	int		 free, error = 0;
93 
94 	/* Check if other command is pending */
95 	if (unit->state & NG_HCI_UNIT_COMMAND_PENDING)
96 		return (0);
97 
98 	/* Check if unit can accept our command */
99 	NG_HCI_BUFF_CMD_GET(unit->buffer, free);
100 	if (free == 0)
101 		return (0);
102 
103 	/* Check if driver hook is still ok */
104 	if (unit->drv == NULL || NG_HOOK_NOT_VALID(unit->drv)) {
105 		NG_HCI_WARN(
106 "%s: %s - hook \"%s\" is not connected or valid\n",
107 			__func__, NG_NODE_NAME(unit->node), NG_HCI_HOOK_DRV);
108 
109 		NG_BT_MBUFQ_DRAIN(&unit->cmdq);
110 
111 		return (ENOTCONN);
112 	}
113 
114 	/*
115 	 * Get first command from queue, give it to RAW hook then
116 	 * make copy of it and send it to the driver
117 	 */
118 
119 	m0 = NG_BT_MBUFQ_FIRST(&unit->cmdq);
120 	if (m0 == NULL)
121 		return (0);
122 
123 	ng_hci_mtap(unit, m0);
124 
125 	m = m_dup(m0, M_NOWAIT);
126 	if (m != NULL)
127 		NG_SEND_DATA_ONLY(error, unit->drv, m);
128 	else
129 		error = ENOBUFS;
130 
131 	if (error != 0)
132 		NG_HCI_ERR(
133 "%s: %s - could not send HCI command, error=%d\n",
134 			__func__, NG_NODE_NAME(unit->node), error);
135 
136 	/*
137 	 * Even if we were not able to send command we still pretend
138 	 * that everything is OK and let timeout handle that.
139 	 */
140 
141 	NG_HCI_BUFF_CMD_USE(unit->buffer, 1);
142 	NG_HCI_STAT_CMD_SENT(unit->stat);
143 	NG_HCI_STAT_BYTES_SENT(unit->stat, m0->m_pkthdr.len);
144 
145 	/*
146 	 * Note: ng_hci_command_timeout() will set
147 	 * NG_HCI_UNIT_COMMAND_PENDING flag
148 	 */
149 
150 	ng_hci_command_timeout(unit);
151 
152 	return (0);
153 } /* ng_hci_send_command */
154 
155 /*
156  * Process HCI Command_Compete event. Complete HCI command, and do post
157  * processing on the command parameters (cp) and command return parameters
158  * (e) if required (for example adjust state).
159  */
160 
161 int
162 ng_hci_process_command_complete(ng_hci_unit_p unit, struct mbuf *e)
163 {
164 	ng_hci_command_compl_ep		*ep = NULL;
165 	struct mbuf			*cp = NULL;
166 	int				 error = 0;
167 
168 	/* Get event packet and update command buffer info */
169 	NG_HCI_M_PULLUP(e, sizeof(*ep));
170 	if (e == NULL)
171 		return (ENOBUFS); /* XXX this is bad */
172 
173 	ep = mtod(e, ng_hci_command_compl_ep *);
174         NG_HCI_BUFF_CMD_SET(unit->buffer, ep->num_cmd_pkts);
175 
176 	/* Check for special NOOP command */
177 	if (ep->opcode == 0x0000) {
178 		NG_FREE_M(e);
179 		goto out;
180 	}
181 
182 	/* Try to match first command item in the queue */
183 	error = complete_command(unit, ep->opcode, &cp);
184 	if (error != 0) {
185 		NG_FREE_M(e);
186 		goto out;
187 	}
188 
189 	/*
190 	 * Perform post processing on command parameters and return parameters
191 	 * do it only if status is OK (status == 0). Status is the first byte
192 	 * of any command return parameters.
193 	 */
194 
195 	ep->opcode = le16toh(ep->opcode);
196 	m_adj(e, sizeof(*ep));
197 
198 	if (*mtod(e, u_int8_t *) == 0) { /* XXX m_pullup here? */
199 		switch (NG_HCI_OGF(ep->opcode)) {
200 		case NG_HCI_OGF_LINK_CONTROL:
201 			error = process_link_control_params(unit,
202 					NG_HCI_OCF(ep->opcode), cp, e);
203 			break;
204 
205 		case NG_HCI_OGF_LINK_POLICY:
206 			error = process_link_policy_params(unit,
207 					NG_HCI_OCF(ep->opcode), cp, e);
208 			break;
209 
210 		case NG_HCI_OGF_HC_BASEBAND:
211 			error = process_hc_baseband_params(unit,
212 					NG_HCI_OCF(ep->opcode), cp, e);
213 			break;
214 
215 		case NG_HCI_OGF_INFO:
216 			error = process_info_params(unit,
217 					NG_HCI_OCF(ep->opcode), cp, e);
218 			break;
219 
220 		case NG_HCI_OGF_STATUS:
221 			error = process_status_params(unit,
222 					NG_HCI_OCF(ep->opcode), cp, e);
223 			break;
224 
225 		case NG_HCI_OGF_TESTING:
226 			error = process_testing_params(unit,
227 					NG_HCI_OCF(ep->opcode), cp, e);
228 			break;
229 		case NG_HCI_OGF_LE:
230 			error = process_le_params(unit,
231 					  NG_HCI_OCF(ep->opcode), cp, e);
232 			break;
233 		case NG_HCI_OGF_BT_LOGO:
234 		case NG_HCI_OGF_VENDOR:
235 			NG_FREE_M(cp);
236 			NG_FREE_M(e);
237 			break;
238 
239 		default:
240 			NG_FREE_M(cp);
241 			NG_FREE_M(e);
242 			error = EINVAL;
243 			break;
244 		}
245 	} else {
246 		NG_HCI_ERR(
247 "%s: %s - HCI command failed, OGF=%#x, OCF=%#x, status=%#x\n",
248 			__func__, NG_NODE_NAME(unit->node),
249 			NG_HCI_OGF(ep->opcode), NG_HCI_OCF(ep->opcode),
250 			*mtod(e, u_int8_t *));
251 
252 		NG_FREE_M(cp);
253 		NG_FREE_M(e);
254 	}
255 out:
256 	ng_hci_send_command(unit);
257 
258 	return (error);
259 } /* ng_hci_process_command_complete */
260 
261 /*
262  * Process HCI Command_Status event. Check the status (mst) and do post
263  * processing (if required).
264  */
265 
266 int
267 ng_hci_process_command_status(ng_hci_unit_p unit, struct mbuf *e)
268 {
269 	ng_hci_command_status_ep	*ep = NULL;
270 	struct mbuf			*cp = NULL;
271 	int				 error = 0;
272 
273 	/* Update command buffer info */
274 	NG_HCI_M_PULLUP(e, sizeof(*ep));
275 	if (e == NULL)
276 		return (ENOBUFS); /* XXX this is bad */
277 
278 	ep = mtod(e, ng_hci_command_status_ep *);
279 	NG_HCI_BUFF_CMD_SET(unit->buffer, ep->num_cmd_pkts);
280 
281 	/* Check for special NOOP command */
282 	if (ep->opcode == 0x0000)
283 		goto out;
284 
285 	/* Try to match first command item in the queue */
286 	error = complete_command(unit, ep->opcode, &cp);
287         if (error != 0)
288 		goto out;
289 
290 	/*
291 	 * Perform post processing on HCI Command_Status event
292 	 */
293 
294 	ep->opcode = le16toh(ep->opcode);
295 
296 	switch (NG_HCI_OGF(ep->opcode)) {
297 	case NG_HCI_OGF_LINK_CONTROL:
298 		error = process_link_control_status(unit, ep, cp);
299 		break;
300 
301 	case NG_HCI_OGF_LINK_POLICY:
302 		error = process_link_policy_status(unit, ep, cp);
303 		break;
304 	case NG_HCI_OGF_LE:
305 		error = process_le_status(unit, ep, cp);
306 		break;
307 	case NG_HCI_OGF_BT_LOGO:
308 	case NG_HCI_OGF_VENDOR:
309 		NG_FREE_M(cp);
310 		break;
311 
312 	case NG_HCI_OGF_HC_BASEBAND:
313 	case NG_HCI_OGF_INFO:
314 	case NG_HCI_OGF_STATUS:
315 	case NG_HCI_OGF_TESTING:
316 	default:
317 		NG_FREE_M(cp);
318 		error = EINVAL;
319 		break;
320 	}
321 out:
322 	NG_FREE_M(e);
323 	ng_hci_send_command(unit);
324 
325 	return (error);
326 } /* ng_hci_process_command_status */
327 
328 /*
329  * Complete queued HCI command.
330  */
331 
332 static int
333 complete_command(ng_hci_unit_p unit, int opcode, struct mbuf **cp)
334 {
335 	struct mbuf	*m = NULL;
336 
337 	/* Check unit state */
338 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) {
339 		NG_HCI_ALERT(
340 "%s: %s - no pending command, state=%#x\n",
341 			__func__, NG_NODE_NAME(unit->node), unit->state);
342 
343 		return (EINVAL);
344 	}
345 
346 	/* Get first command in the queue */
347 	m = NG_BT_MBUFQ_FIRST(&unit->cmdq);
348 	if (m == NULL) {
349 		NG_HCI_ALERT(
350 "%s: %s - empty command queue?!\n", __func__, NG_NODE_NAME(unit->node));
351 
352 		return (EINVAL);
353 	}
354 
355 	/*
356 	 * Match command opcode, if does not match - do nothing and
357 	 * let timeout handle that.
358 	 */
359 
360 	if (mtod(m, ng_hci_cmd_pkt_t *)->opcode != opcode) {
361 		NG_HCI_ALERT(
362 "%s: %s - command queue is out of sync\n", __func__, NG_NODE_NAME(unit->node));
363 
364 		return (EINVAL);
365 	}
366 
367 	/*
368 	 * Now we can remove command timeout, dequeue completed command
369 	 * and return command parameters. ng_hci_command_untimeout will
370 	 * drop NG_HCI_UNIT_COMMAND_PENDING flag.
371 	 * Note: if ng_hci_command_untimeout() fails (returns non-zero)
372 	 * then timeout already happened and timeout message went info node
373 	 * queue. In this case we ignore command completion and pretend
374 	 * there is a timeout.
375 	 */
376 
377 	if (ng_hci_command_untimeout(unit) != 0)
378 		return (ETIMEDOUT);
379 
380 	NG_BT_MBUFQ_DEQUEUE(&unit->cmdq, *cp);
381 	m_adj(*cp, sizeof(ng_hci_cmd_pkt_t));
382 
383 	return (0);
384 } /* complete_command */
385 
386 /*
387  * Process HCI command timeout
388  */
389 
390 void
391 ng_hci_process_command_timeout(node_p node, hook_p hook, void *arg1, int arg2)
392 {
393 	ng_hci_unit_p	 unit = NULL;
394 	struct mbuf	*m = NULL;
395 	u_int16_t	 opcode;
396 
397 	if (NG_NODE_NOT_VALID(node)) {
398 		printf("%s: Netgraph node is not valid\n", __func__);
399 		return;
400 	}
401 
402 	unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
403 
404 	if (unit->state & NG_HCI_UNIT_COMMAND_PENDING) {
405 		unit->state &= ~NG_HCI_UNIT_COMMAND_PENDING;
406 
407 		NG_BT_MBUFQ_DEQUEUE(&unit->cmdq, m);
408 		if (m == NULL) {
409 			NG_HCI_ALERT(
410 "%s: %s - command queue is out of sync!\n", __func__, NG_NODE_NAME(unit->node));
411 
412 			return;
413 		}
414 
415 		opcode = le16toh(mtod(m, ng_hci_cmd_pkt_t *)->opcode);
416 		NG_FREE_M(m);
417 
418 		NG_HCI_ERR(
419 "%s: %s - unable to complete HCI command OGF=%#x, OCF=%#x. Timeout\n",
420 			__func__, NG_NODE_NAME(unit->node), NG_HCI_OGF(opcode),
421 			NG_HCI_OCF(opcode));
422 
423 		/* Try to send more commands */
424  		NG_HCI_BUFF_CMD_SET(unit->buffer, 1);
425 		ng_hci_send_command(unit);
426 	} else
427 		NG_HCI_ALERT(
428 "%s: %s - no pending command\n", __func__, NG_NODE_NAME(unit->node));
429 } /* ng_hci_process_command_timeout */
430 
431 /*
432  * Process link command return parameters
433  */
434 
435 static int
436 process_link_control_params(ng_hci_unit_p unit, u_int16_t ocf,
437 		struct mbuf *mcp, struct mbuf *mrp)
438 {
439 	int	error  = 0;
440 
441 	switch (ocf) {
442 	case NG_HCI_OCF_INQUIRY_CANCEL:
443 	case NG_HCI_OCF_PERIODIC_INQUIRY:
444 	case NG_HCI_OCF_EXIT_PERIODIC_INQUIRY:
445 	case NG_HCI_OCF_LINK_KEY_REP:
446 	case NG_HCI_OCF_LINK_KEY_NEG_REP:
447 	case NG_HCI_OCF_PIN_CODE_REP:
448 	case NG_HCI_OCF_PIN_CODE_NEG_REP:
449 		/* These do not need post processing */
450 		break;
451 
452 	case NG_HCI_OCF_INQUIRY:
453 	case NG_HCI_OCF_CREATE_CON:
454 	case NG_HCI_OCF_DISCON:
455 	case NG_HCI_OCF_ADD_SCO_CON:
456 	case NG_HCI_OCF_ACCEPT_CON:
457 	case NG_HCI_OCF_REJECT_CON:
458 	case NG_HCI_OCF_CHANGE_CON_PKT_TYPE:
459 	case NG_HCI_OCF_AUTH_REQ:
460 	case NG_HCI_OCF_SET_CON_ENCRYPTION:
461 	case NG_HCI_OCF_CHANGE_CON_LINK_KEY:
462 	case NG_HCI_OCF_MASTER_LINK_KEY:
463 	case NG_HCI_OCF_REMOTE_NAME_REQ:
464 	case NG_HCI_OCF_READ_REMOTE_FEATURES:
465 	case NG_HCI_OCF_READ_REMOTE_VER_INFO:
466 	case NG_HCI_OCF_READ_CLOCK_OFFSET:
467 	default:
468 
469 		/*
470 		 * None of these command was supposed to generate
471 		 * Command_Complete event. Instead Command_Status event
472 		 * should have been generated and then appropriate event
473 		 * should have been sent to indicate the final result.
474 		 */
475 
476 		error = EINVAL;
477 		break;
478 	}
479 
480 	NG_FREE_M(mcp);
481 	NG_FREE_M(mrp);
482 
483 	return (error);
484 } /* process_link_control_params */
485 
486 /*
487  * Process link policy command return parameters
488  */
489 
490 static int
491 process_link_policy_params(ng_hci_unit_p unit, u_int16_t ocf,
492 		struct mbuf *mcp, struct mbuf *mrp)
493 {
494 	int	error = 0;
495 
496 	switch (ocf){
497 	case NG_HCI_OCF_ROLE_DISCOVERY: {
498 		ng_hci_role_discovery_rp	*rp = NULL;
499 		ng_hci_unit_con_t		*con = NULL;
500 		u_int16_t			 h;
501 
502 		NG_HCI_M_PULLUP(mrp, sizeof(*rp));
503 		if (mrp != NULL) {
504 			rp = mtod(mrp, ng_hci_role_discovery_rp *);
505 
506 			h = NG_HCI_CON_HANDLE(le16toh(rp->con_handle));
507 			con = ng_hci_con_by_handle(unit, h);
508 			if (con == NULL) {
509 				NG_HCI_ALERT(
510 "%s: %s - invalid connection handle=%d\n",
511 					__func__, NG_NODE_NAME(unit->node), h);
512 				error = ENOENT;
513 			} else if (con->link_type != NG_HCI_LINK_ACL) {
514 				NG_HCI_ALERT(
515 "%s: %s - invalid link type=%d\n", __func__, NG_NODE_NAME(unit->node),
516 					con->link_type);
517 				error = EINVAL;
518 			} else
519 				con->role = rp->role;
520 		} else
521 			error = ENOBUFS;
522 		} break;
523 
524 	case NG_HCI_OCF_READ_LINK_POLICY_SETTINGS:
525 	case NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS:
526 		/* These do not need post processing */
527 		break;
528 
529 	case NG_HCI_OCF_HOLD_MODE:
530 	case NG_HCI_OCF_SNIFF_MODE:
531 	case NG_HCI_OCF_EXIT_SNIFF_MODE:
532 	case NG_HCI_OCF_PARK_MODE:
533 	case NG_HCI_OCF_EXIT_PARK_MODE:
534 	case NG_HCI_OCF_QOS_SETUP:
535 	case NG_HCI_OCF_SWITCH_ROLE:
536 	default:
537 
538 		/*
539 		 * None of these command was supposed to generate
540 		 * Command_Complete event. Instead Command_Status event
541 		 * should have been generated and then appropriate event
542 		 * should have been sent to indicate the final result.
543 		 */
544 
545 		error = EINVAL;
546 		break;
547 	}
548 
549 	NG_FREE_M(mcp);
550 	NG_FREE_M(mrp);
551 
552 	return (error);
553 } /* process_link_policy_params */
554 
555 /*
556  * Process HC and baseband command return parameters
557  */
558 
559 int
560 process_hc_baseband_params(ng_hci_unit_p unit, u_int16_t ocf,
561 		struct mbuf *mcp, struct mbuf *mrp)
562 {
563 	int	error = 0;
564 
565 	switch (ocf) {
566 	case NG_HCI_OCF_SET_EVENT_MASK:
567 	case NG_HCI_OCF_SET_EVENT_FILTER:
568 	case NG_HCI_OCF_FLUSH:	/* XXX Do we need to handle that? */
569 	case NG_HCI_OCF_READ_PIN_TYPE:
570 	case NG_HCI_OCF_WRITE_PIN_TYPE:
571 	case NG_HCI_OCF_CREATE_NEW_UNIT_KEY:
572 	case NG_HCI_OCF_WRITE_STORED_LINK_KEY:
573 	case NG_HCI_OCF_WRITE_CON_ACCEPT_TIMO:
574 	case NG_HCI_OCF_WRITE_PAGE_TIMO:
575 	case NG_HCI_OCF_READ_SCAN_ENABLE:
576 	case NG_HCI_OCF_WRITE_SCAN_ENABLE:
577 	case NG_HCI_OCF_WRITE_PAGE_SCAN_ACTIVITY:
578 	case NG_HCI_OCF_WRITE_INQUIRY_SCAN_ACTIVITY:
579 	case NG_HCI_OCF_READ_AUTH_ENABLE:
580 	case NG_HCI_OCF_WRITE_AUTH_ENABLE:
581 	case NG_HCI_OCF_READ_ENCRYPTION_MODE:
582 	case NG_HCI_OCF_WRITE_ENCRYPTION_MODE:
583 	case NG_HCI_OCF_WRITE_VOICE_SETTINGS:
584 	case NG_HCI_OCF_READ_NUM_BROADCAST_RETRANS:
585 	case NG_HCI_OCF_WRITE_NUM_BROADCAST_RETRANS:
586 	case NG_HCI_OCF_READ_HOLD_MODE_ACTIVITY:
587 	case NG_HCI_OCF_WRITE_HOLD_MODE_ACTIVITY:
588 	case NG_HCI_OCF_READ_SCO_FLOW_CONTROL:
589 	case NG_HCI_OCF_WRITE_SCO_FLOW_CONTROL:
590 	case NG_HCI_OCF_H2HC_FLOW_CONTROL: /* XXX Not supported this time */
591 	case NG_HCI_OCF_HOST_BUFFER_SIZE:
592 	case NG_HCI_OCF_READ_IAC_LAP:
593 	case NG_HCI_OCF_WRITE_IAC_LAP:
594 	case NG_HCI_OCF_READ_PAGE_SCAN_PERIOD:
595 	case NG_HCI_OCF_WRITE_PAGE_SCAN_PERIOD:
596 	case NG_HCI_OCF_READ_PAGE_SCAN:
597 	case NG_HCI_OCF_WRITE_PAGE_SCAN:
598 	case NG_HCI_OCF_READ_LINK_SUPERVISION_TIMO:
599 	case NG_HCI_OCF_WRITE_LINK_SUPERVISION_TIMO:
600 	case NG_HCI_OCF_READ_SUPPORTED_IAC_NUM:
601 	case NG_HCI_OCF_READ_STORED_LINK_KEY:
602 	case NG_HCI_OCF_DELETE_STORED_LINK_KEY:
603 	case NG_HCI_OCF_READ_CON_ACCEPT_TIMO:
604 	case NG_HCI_OCF_READ_PAGE_TIMO:
605 	case NG_HCI_OCF_READ_PAGE_SCAN_ACTIVITY:
606 	case NG_HCI_OCF_READ_INQUIRY_SCAN_ACTIVITY:
607 	case NG_HCI_OCF_READ_VOICE_SETTINGS:
608 	case NG_HCI_OCF_READ_AUTO_FLUSH_TIMO:
609 	case NG_HCI_OCF_WRITE_AUTO_FLUSH_TIMO:
610 	case NG_HCI_OCF_READ_XMIT_LEVEL:
611 	case NG_HCI_OCF_HOST_NUM_COMPL_PKTS:	/* XXX Can get here? */
612 	case NG_HCI_OCF_CHANGE_LOCAL_NAME:
613 	case NG_HCI_OCF_READ_LOCAL_NAME:
614 	case NG_HCI_OCF_READ_UNIT_CLASS:
615 	case NG_HCI_OCF_WRITE_UNIT_CLASS:
616 	case NG_HCI_OCF_READ_LE_HOST_SUPPORTED:
617 	case NG_HCI_OCF_WRITE_LE_HOST_SUPPORTED:
618 		/* These do not need post processing */
619 		break;
620 
621 	case NG_HCI_OCF_RESET: {
622 		ng_hci_unit_con_p	con = NULL;
623 		int			size;
624 
625 		/*
626 		 * XXX
627 		 *
628 		 * After RESET command unit goes into standby mode
629 		 * and all operational state is lost. Host controller
630 		 * will revert to default values for all parameters.
631 		 *
632 		 * For now we shall terminate all connections and drop
633 		 * inited bit. After RESET unit must be re-initialized.
634 		 */
635 
636 		while (!LIST_EMPTY(&unit->con_list)) {
637 			con = LIST_FIRST(&unit->con_list);
638 
639 			/* Remove all timeouts (if any) */
640 			if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
641 				ng_hci_con_untimeout(con);
642 
643 			/* Connection terminated by local host */
644 			ng_hci_lp_discon_ind(con, 0x16);
645 			ng_hci_free_con(con);
646 		}
647 
648 		NG_HCI_BUFF_ACL_TOTAL(unit->buffer, size);
649 		NG_HCI_BUFF_ACL_FREE(unit->buffer, size);
650 
651 		NG_HCI_BUFF_SCO_TOTAL(unit->buffer, size);
652 		NG_HCI_BUFF_SCO_FREE(unit->buffer, size);
653 
654 		unit->state &= ~NG_HCI_UNIT_INITED;
655 		} break;
656 
657 	default:
658 		error = EINVAL;
659 		break;
660 	}
661 
662 	NG_FREE_M(mcp);
663 	NG_FREE_M(mrp);
664 
665 	return (error);
666 } /* process_hc_baseband_params */
667 
668 /*
669  * Process info command return parameters
670  */
671 
672 static int
673 process_info_params(ng_hci_unit_p unit, u_int16_t ocf, struct mbuf *mcp,
674 		struct mbuf *mrp)
675 {
676 	int	error = 0, len;
677 
678 	switch (ocf) {
679 	case NG_HCI_OCF_READ_LOCAL_VER:
680 	case NG_HCI_OCF_READ_COUNTRY_CODE:
681 		break;
682 
683 	case NG_HCI_OCF_READ_LOCAL_FEATURES:
684 		m_adj(mrp, sizeof(u_int8_t));
685 		len = min(mrp->m_pkthdr.len, sizeof(unit->features));
686 		m_copydata(mrp, 0, len, (caddr_t) unit->features);
687 		break;
688 
689 	case NG_HCI_OCF_READ_BUFFER_SIZE: {
690 		ng_hci_read_buffer_size_rp	*rp = NULL;
691 
692 		/* Do not update buffer descriptor if node was initialized */
693 		if ((unit->state & NG_HCI_UNIT_READY) == NG_HCI_UNIT_READY)
694 			break;
695 
696 		NG_HCI_M_PULLUP(mrp, sizeof(*rp));
697 		if (mrp != NULL) {
698 			rp = mtod(mrp, ng_hci_read_buffer_size_rp *);
699 
700 			NG_HCI_BUFF_ACL_SET(
701 				unit->buffer,
702 				le16toh(rp->num_acl_pkt),  /* number */
703 				le16toh(rp->max_acl_size), /* size */
704 				le16toh(rp->num_acl_pkt)   /* free */
705 			);
706 
707 			NG_HCI_BUFF_SCO_SET(
708 				unit->buffer,
709 				le16toh(rp->num_sco_pkt), /* number */
710 				rp->max_sco_size,         /* size */
711 				le16toh(rp->num_sco_pkt)  /* free */
712 			);
713 
714 			/* Let upper layers know */
715 			ng_hci_node_is_up(unit->node, unit->acl, NULL, 0);
716 			ng_hci_node_is_up(unit->node, unit->sco, NULL, 0);
717 		} else
718 			error = ENOBUFS;
719 		} break;
720 
721 	case NG_HCI_OCF_READ_BDADDR:
722 		/* Do not update BD_ADDR if node was initialized */
723 		if ((unit->state & NG_HCI_UNIT_READY) == NG_HCI_UNIT_READY)
724 			break;
725 
726 		m_adj(mrp, sizeof(u_int8_t));
727 		len = min(mrp->m_pkthdr.len, sizeof(unit->bdaddr));
728 		m_copydata(mrp, 0, len, (caddr_t) &unit->bdaddr);
729 
730 		/* Let upper layers know */
731 		ng_hci_node_is_up(unit->node, unit->acl, NULL, 0);
732 		ng_hci_node_is_up(unit->node, unit->sco, NULL, 0);
733 		break;
734 
735 	default:
736 		error = EINVAL;
737 		break;
738 	}
739 
740 	NG_FREE_M(mcp);
741 	NG_FREE_M(mrp);
742 
743 	return (error);
744 } /* process_info_params */
745 
746 /*
747  * Process status command return parameters
748  */
749 
750 static int
751 process_status_params(ng_hci_unit_p unit, u_int16_t ocf, struct mbuf *mcp,
752 		struct mbuf *mrp)
753 {
754 	int	error = 0;
755 
756 	switch (ocf) {
757 	case NG_HCI_OCF_READ_FAILED_CONTACT_CNTR:
758 	case NG_HCI_OCF_RESET_FAILED_CONTACT_CNTR:
759 	case NG_HCI_OCF_GET_LINK_QUALITY:
760 	case NG_HCI_OCF_READ_RSSI:
761 		/* These do not need post processing */
762 		break;
763 
764 	default:
765 		error = EINVAL;
766 		break;
767 	}
768 
769 	NG_FREE_M(mcp);
770 	NG_FREE_M(mrp);
771 
772 	return (error);
773 } /* process_status_params */
774 
775 /*
776  * Process testing command return parameters
777  */
778 
779 int
780 process_testing_params(ng_hci_unit_p unit, u_int16_t ocf, struct mbuf *mcp,
781 		struct mbuf *mrp)
782 {
783 	int	error = 0;
784 
785 	switch (ocf) {
786 
787 	/*
788 	 * XXX FIXME
789 	 * We do not support these features at this time. However,
790 	 * HCI node could support this and do something smart. At least
791 	 * node can change unit state.
792 	 */
793 
794 	case NG_HCI_OCF_READ_LOOPBACK_MODE:
795 	case NG_HCI_OCF_WRITE_LOOPBACK_MODE:
796 	case NG_HCI_OCF_ENABLE_UNIT_UNDER_TEST:
797 		break;
798 
799 	default:
800 		error = EINVAL;
801 		break;
802 	}
803 
804 	NG_FREE_M(mcp);
805 	NG_FREE_M(mrp);
806 
807 	return (error);
808 } /* process_testing_params */
809 
810 /*
811  * Process LE command return parameters
812  */
813 
814 static int
815 process_le_params(ng_hci_unit_p unit, u_int16_t ocf,
816 		struct mbuf *mcp, struct mbuf *mrp)
817 {
818 	int	error = 0;
819 
820 	switch (ocf){
821 	case NG_HCI_OCF_LE_SET_EVENT_MASK:
822 	case NG_HCI_OCF_LE_READ_BUFFER_SIZE:
823 	case NG_HCI_OCF_LE_READ_LOCAL_SUPPORTED_FEATURES:
824 	case NG_HCI_OCF_LE_SET_RANDOM_ADDRESS:
825 	case NG_HCI_OCF_LE_SET_ADVERTISING_PARAMETERS:
826 	case NG_HCI_OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER:
827 	case NG_HCI_OCF_LE_SET_ADVERTISING_DATA:
828 	case NG_HCI_OCF_LE_SET_SCAN_RESPONSE_DATA:
829 	case NG_HCI_OCF_LE_SET_ADVERTISE_ENABLE:
830 	case NG_HCI_OCF_LE_SET_SCAN_PARAMETERS:
831 	case NG_HCI_OCF_LE_SET_SCAN_ENABLE:
832 	case NG_HCI_OCF_LE_CREATE_CONNECTION_CANCEL:
833 	case NG_HCI_OCF_LE_CLEAR_WHITE_LIST:
834 	case NG_HCI_OCF_LE_READ_WHITE_LIST_SIZE:
835 	case NG_HCI_OCF_LE_ADD_DEVICE_TO_WHITE_LIST:
836 	case NG_HCI_OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST:
837 	case NG_HCI_OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION:
838 	case NG_HCI_OCF_LE_READ_CHANNEL_MAP:
839 	case NG_HCI_OCF_LE_ENCRYPT:
840 	case NG_HCI_OCF_LE_RAND:
841 	case NG_HCI_OCF_LE_LONG_TERM_KEY_REQUEST_REPLY:
842 	case NG_HCI_OCF_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY:
843 	case NG_HCI_OCF_LE_READ_SUPPORTED_STATUS:
844 	case NG_HCI_OCF_LE_RECEIVER_TEST:
845 	case NG_HCI_OCF_LE_TRANSMITTER_TEST:
846 	case NG_HCI_OCF_LE_TEST_END:
847 
848 		/* These do not need post processing */
849 		break;
850 	case NG_HCI_OCF_LE_CREATE_CONNECTION:
851 	case NG_HCI_OCF_LE_CONNECTION_UPDATE:
852 	case NG_HCI_OCF_LE_READ_REMOTE_USED_FEATURES:
853 	case NG_HCI_OCF_LE_START_ENCRYPTION:
854 
855 
856 	default:
857 		/*
858 		 * None of these command was supposed to generate
859 		 * Command_Complete event. Instead Command_Status event
860 		 * should have been generated and then appropriate event
861 		 * should have been sent to indicate the final result.
862 		 */
863 
864 		error = EINVAL;
865 		break;
866 	}
867 
868 	NG_FREE_M(mcp);
869 	NG_FREE_M(mrp);
870 
871 	return (error);
872 
873 }
874 
875 
876 
877 static int
878 process_le_status(ng_hci_unit_p unit,ng_hci_command_status_ep *ep,
879 		struct mbuf *mcp)
880 {
881 	int	error = 0;
882 
883 	switch (NG_HCI_OCF(ep->opcode)){
884 	case NG_HCI_OCF_LE_CREATE_CONNECTION:
885 	case NG_HCI_OCF_LE_CONNECTION_UPDATE:
886 	case NG_HCI_OCF_LE_READ_REMOTE_USED_FEATURES:
887 	case NG_HCI_OCF_LE_START_ENCRYPTION:
888 
889 		/* These do not need post processing */
890 		break;
891 
892 	case NG_HCI_OCF_LE_SET_EVENT_MASK:
893 	case NG_HCI_OCF_LE_READ_BUFFER_SIZE:
894 	case NG_HCI_OCF_LE_READ_LOCAL_SUPPORTED_FEATURES:
895 	case NG_HCI_OCF_LE_SET_RANDOM_ADDRESS:
896 	case NG_HCI_OCF_LE_SET_ADVERTISING_PARAMETERS:
897 	case NG_HCI_OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER:
898 	case NG_HCI_OCF_LE_SET_ADVERTISING_DATA:
899 	case NG_HCI_OCF_LE_SET_SCAN_RESPONSE_DATA:
900 	case NG_HCI_OCF_LE_SET_ADVERTISE_ENABLE:
901 	case NG_HCI_OCF_LE_SET_SCAN_PARAMETERS:
902 	case NG_HCI_OCF_LE_SET_SCAN_ENABLE:
903 	case NG_HCI_OCF_LE_CREATE_CONNECTION_CANCEL:
904 	case NG_HCI_OCF_LE_CLEAR_WHITE_LIST:
905 	case NG_HCI_OCF_LE_READ_WHITE_LIST_SIZE:
906 	case NG_HCI_OCF_LE_ADD_DEVICE_TO_WHITE_LIST:
907 	case NG_HCI_OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST:
908 	case NG_HCI_OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION:
909 	case NG_HCI_OCF_LE_READ_CHANNEL_MAP:
910 	case NG_HCI_OCF_LE_ENCRYPT:
911 	case NG_HCI_OCF_LE_RAND:
912 	case NG_HCI_OCF_LE_LONG_TERM_KEY_REQUEST_REPLY:
913 	case NG_HCI_OCF_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY:
914 	case NG_HCI_OCF_LE_READ_SUPPORTED_STATUS:
915 	case NG_HCI_OCF_LE_RECEIVER_TEST:
916 	case NG_HCI_OCF_LE_TRANSMITTER_TEST:
917 	case NG_HCI_OCF_LE_TEST_END:
918 
919 
920 	default:
921 		/*
922 		 * None of these command was supposed to generate
923 		 * Command_Stutus event. Command Complete instead.
924 		 */
925 
926 		error = EINVAL;
927 		break;
928 	}
929 
930 	NG_FREE_M(mcp);
931 
932 	return (error);
933 
934 }
935 
936 /*
937  * Process link control command status
938  */
939 
940 static int
941 process_link_control_status(ng_hci_unit_p unit, ng_hci_command_status_ep *ep,
942 		struct mbuf *mcp)
943 {
944 	int	error = 0;
945 
946 	switch (NG_HCI_OCF(ep->opcode)) {
947 	case NG_HCI_OCF_INQUIRY:
948 	case NG_HCI_OCF_DISCON:		/* XXX */
949 	case NG_HCI_OCF_REJECT_CON:	/* XXX */
950 	case NG_HCI_OCF_CHANGE_CON_PKT_TYPE:
951 	case NG_HCI_OCF_AUTH_REQ:
952 	case NG_HCI_OCF_SET_CON_ENCRYPTION:
953 	case NG_HCI_OCF_CHANGE_CON_LINK_KEY:
954 	case NG_HCI_OCF_MASTER_LINK_KEY:
955 	case NG_HCI_OCF_REMOTE_NAME_REQ:
956 	case NG_HCI_OCF_READ_REMOTE_FEATURES:
957 	case NG_HCI_OCF_READ_REMOTE_VER_INFO:
958 	case NG_HCI_OCF_READ_CLOCK_OFFSET:
959 		/* These do not need post processing */
960 		break;
961 
962 	case NG_HCI_OCF_CREATE_CON:
963 		break;
964 
965 	case NG_HCI_OCF_ADD_SCO_CON:
966 		break;
967 
968 	case NG_HCI_OCF_ACCEPT_CON:
969 		break;
970 
971 	case NG_HCI_OCF_INQUIRY_CANCEL:
972 	case NG_HCI_OCF_PERIODIC_INQUIRY:
973 	case NG_HCI_OCF_EXIT_PERIODIC_INQUIRY:
974 	case NG_HCI_OCF_LINK_KEY_REP:
975 	case NG_HCI_OCF_LINK_KEY_NEG_REP:
976 	case NG_HCI_OCF_PIN_CODE_REP:
977 	case NG_HCI_OCF_PIN_CODE_NEG_REP:
978 	default:
979 
980 		/*
981 		 * None of these command was supposed to generate
982 		 * Command_Status event. Instead Command_Complete event
983 		 * should have been sent.
984 		 */
985 
986 		error = EINVAL;
987 		break;
988 	}
989 
990 	NG_FREE_M(mcp);
991 
992 	return (error);
993 } /* process_link_control_status */
994 
995 /*
996  * Process link policy command status
997  */
998 
999 static int
1000 process_link_policy_status(ng_hci_unit_p unit, ng_hci_command_status_ep *ep,
1001 		struct mbuf *mcp)
1002 {
1003 	int	error = 0;
1004 
1005 	switch (NG_HCI_OCF(ep->opcode)) {
1006 	case NG_HCI_OCF_HOLD_MODE:
1007 	case NG_HCI_OCF_SNIFF_MODE:
1008 	case NG_HCI_OCF_EXIT_SNIFF_MODE:
1009 	case NG_HCI_OCF_PARK_MODE:
1010 	case NG_HCI_OCF_EXIT_PARK_MODE:
1011 	case NG_HCI_OCF_SWITCH_ROLE:
1012 		/* These do not need post processing */
1013 		break;
1014 
1015 	case NG_HCI_OCF_QOS_SETUP:
1016 		break;
1017 
1018 	case NG_HCI_OCF_ROLE_DISCOVERY:
1019 	case NG_HCI_OCF_READ_LINK_POLICY_SETTINGS:
1020 	case NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS:
1021 	default:
1022 
1023 		/*
1024 		 * None of these command was supposed to generate
1025 		 * Command_Status event. Instead Command_Complete event
1026 		 * should have been sent.
1027 		 */
1028 
1029 		error = EINVAL;
1030 		break;
1031 	}
1032 
1033 	NG_FREE_M(mcp);
1034 
1035 	return (error);
1036 } /* process_link_policy_status */
1037 
1038