xref: /freebsd/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c (revision 5036d9652a5701d00e9e40ea942c278e9f77d33d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
5  * Copyright (c) 2023 Future Crew LLC.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/endian.h>
31 #include <sys/stat.h>
32 
33 #include <assert.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <stddef.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <time.h>
41 #include <unistd.h>
42 
43 #include <libusb.h>
44 
45 #include <netgraph/bluetooth/include/ng_hci.h>
46 
47 #include "iwmbt_fw.h"
48 #include "iwmbt_hw.h"
49 #include "iwmbt_dbg.h"
50 
51 #define	XMIN(x, y)	((x) < (y) ? (x) : (y))
52 
53 static int
54 iwmbt_send_fragment(struct libusb_device_handle *hdl,
55     uint8_t fragment_type, const void *data, uint8_t len, int timeout)
56 {
57 	int ret, transferred;
58 	uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
59 	struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *) buf;
60 
61 	memset(buf, 0, sizeof(buf));
62 	cmd->opcode = htole16(0xfc09),
63 	cmd->length = len + 1,
64 	cmd->data[0] = fragment_type;
65 	memcpy(cmd->data + 1, data, len);
66 
67 	ret = libusb_bulk_transfer(hdl,
68 	    IWMBT_BULK_OUT_ENDPOINT_ADDR,
69 	    (uint8_t *)cmd,
70 	    IWMBT_HCI_CMD_SIZE(cmd),
71 	    &transferred,
72 	    timeout);
73 
74 	if (ret < 0 || transferred != (int)IWMBT_HCI_CMD_SIZE(cmd)) {
75 		iwmbt_err("libusb_bulk_transfer() failed: err=%s, size=%zu",
76 		    libusb_strerror(ret),
77 		    IWMBT_HCI_CMD_SIZE(cmd));
78 		return (-1);
79 	}
80 
81 	ret = libusb_bulk_transfer(hdl,
82 	    IWMBT_BULK_IN_ENDPOINT_ADDR,
83 	    buf,
84 	    sizeof(buf),
85 	    &transferred,
86 	    timeout);
87 
88 	if (ret < 0) {
89 		iwmbt_err("libusb_bulk_transfer() failed: err=%s",
90 		    libusb_strerror(ret));
91 		return (-1);
92 	}
93 
94 	return (0);
95 }
96 
97 static int
98 iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
99     void *event, int size, int *transferred, int timeout)
100 {
101 	struct timespec to, now, remains;
102 	int ret;
103 
104 	ret = libusb_control_transfer(hdl,
105 	    LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
106 	    0,
107 	    0,
108 	    0,
109 	    (uint8_t *)cmd,
110 	    IWMBT_HCI_CMD_SIZE(cmd),
111 	    timeout);
112 
113 	if (ret < 0) {
114 		iwmbt_err("libusb_control_transfer() failed: err=%s",
115 		    libusb_strerror(ret));
116 		return (ret);
117 	}
118 
119 	clock_gettime(CLOCK_MONOTONIC, &now);
120 	to = IWMBT_MSEC2TS(timeout);
121 	timespecadd(&to, &now, &to);
122 
123 	do {
124 		timespecsub(&to, &now, &remains);
125 		ret = libusb_interrupt_transfer(hdl,
126 		    IWMBT_INTERRUPT_ENDPOINT_ADDR,
127 		    event,
128 		    size,
129 		    transferred,
130 		    IWMBT_TS2MSEC(remains) + 1);
131 
132 		if (ret < 0) {
133 			iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
134 			    libusb_strerror(ret));
135 			return (ret);
136 		}
137 
138 		switch (((struct iwmbt_hci_event *)event)->header.event) {
139 		case NG_HCI_EVENT_COMMAND_COMPL:
140 			if (*transferred <
141 			    (int)offsetof(struct iwmbt_hci_event_cmd_compl, data))
142 				break;
143 			if (cmd->opcode !=
144 			    ((struct iwmbt_hci_event_cmd_compl *)event)->opcode)
145 				break;
146 			/* FALLTHROUGH */
147 		case 0xFF:
148 			return (0);
149 		default:
150 			break;
151 		}
152 		iwmbt_debug("Stray HCI event: %x",
153 		    ((struct iwmbt_hci_event *)event)->header.event);
154 	} while (timespeccmp(&to, &now, >));
155 
156 	iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
157 	    libusb_strerror(LIBUSB_ERROR_TIMEOUT));
158 
159 	return (LIBUSB_ERROR_TIMEOUT);
160 }
161 
162 int
163 iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
164     const struct iwmbt_firmware *fw)
165 {
166 	int ret, transferred;
167 	struct iwmbt_firmware fw_job = *fw;
168 	uint16_t cmd_opcode;
169 	uint8_t cmd_length;
170 	struct iwmbt_hci_cmd *cmd_buf;
171 	uint8_t evt_code;
172 	uint8_t evt_length;
173 	uint8_t evt_buf[IWMBT_HCI_MAX_EVENT_SIZE];
174 	int activate_patch = 0;
175 
176 	while (fw_job.len > 0) {
177 		if (fw_job.len < 4) {
178 			iwmbt_err("Invalid firmware, unexpected EOF in HCI "
179 			    "command header. Remains=%d", fw_job.len);
180 			return (-1);
181 		}
182 
183 		if (fw_job.buf[0] != 0x01) {
184 			iwmbt_err("Invalid firmware, expected HCI command (%d)",
185 					fw_job.buf[0]);
186 			return (-1);
187 		}
188 
189 		/* Advance by one. */
190 		fw_job.buf++;
191 		fw_job.len--;
192 
193 		/* Load in the HCI command to perform. */
194 		cmd_opcode = le16dec(fw_job.buf);
195 		cmd_length = fw_job.buf[2];
196 		cmd_buf = (struct iwmbt_hci_cmd *)fw_job.buf;
197 
198 		iwmbt_debug("opcode=%04x, len=%02x", cmd_opcode, cmd_length);
199 
200 		/*
201 		 * If there is a command that loads a patch in the
202 		 * firmware file, then activate the patch upon success,
203 		 * otherwise just disable the manufacturer mode.
204 		 */
205 		if (cmd_opcode == 0xfc8e)
206 			activate_patch = 1;
207 
208 		/* Advance by three. */
209 		fw_job.buf += 3;
210 		fw_job.len -= 3;
211 
212 		if (fw_job.len < cmd_length) {
213 			iwmbt_err("Invalid firmware, unexpected EOF in HCI "
214 			    "command data. len=%d, remains=%d",
215 			    cmd_length, fw_job.len);
216 			return (-1);
217 		}
218 
219 		/* Advance by data length. */
220 		fw_job.buf += cmd_length;
221 		fw_job.len -= cmd_length;
222 
223 		ret = libusb_control_transfer(hdl,
224 		    LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
225 		    0,
226 		    0,
227 		    0,
228 		    (uint8_t *)cmd_buf,
229 		    IWMBT_HCI_CMD_SIZE(cmd_buf),
230 		    IWMBT_HCI_CMD_TIMEOUT);
231 
232 		if (ret < 0) {
233 			iwmbt_err("libusb_control_transfer() failed: err=%s",
234 			    libusb_strerror(ret));
235 			return (-1);
236 		}
237 
238 		/*
239 		 * Every command has its associated event: data must match
240 		 * what is recorded in the firmware file. Perform that check
241 		 * now.
242 		 */
243 
244 		while (fw_job.len > 0 && fw_job.buf[0] == 0x02) {
245 			/* Is this the end of the file? */
246 			if (fw_job.len < 3) {
247 				iwmbt_err("Invalid firmware, unexpected EOF in"
248 				    "event header. remains=%d", fw_job.len);
249 				return (-1);
250 			}
251 
252 			/* Advance by one. */
253 			fw_job.buf++;
254 			fw_job.len--;
255 
256 			/* Load in the HCI event. */
257 			evt_code = fw_job.buf[0];
258 			evt_length = fw_job.buf[1];
259 
260 			/* Advance by two. */
261 			fw_job.buf += 2;
262 			fw_job.len -= 2;
263 
264 			/* Prepare HCI event buffer. */
265 			memset(evt_buf, 0, IWMBT_HCI_MAX_EVENT_SIZE);
266 
267 			iwmbt_debug("event=%04x, len=%02x",
268 					evt_code, evt_length);
269 
270 			if (fw_job.len < evt_length) {
271 				iwmbt_err("Invalid firmware, unexpected EOF in"
272 				    " event data. len=%d, remains=%d",
273 				    evt_length, fw_job.len);
274 				return (-1);
275 			}
276 
277 			ret = libusb_interrupt_transfer(hdl,
278 			    IWMBT_INTERRUPT_ENDPOINT_ADDR,
279 			    evt_buf,
280 			    IWMBT_HCI_MAX_EVENT_SIZE,
281 			    &transferred,
282 			    IWMBT_HCI_CMD_TIMEOUT);
283 
284 			if (ret < 0) {
285 				iwmbt_err("libusb_interrupt_transfer() failed:"
286 				    " err=%s", libusb_strerror(ret));
287 				return (-1);
288 			}
289 
290 			if ((int)evt_length + 2 != transferred ||
291 			    memcmp(evt_buf + 2, fw_job.buf, evt_length) != 0) {
292 				iwmbt_err("event does not match firmware");
293 				return (-1);
294 			}
295 
296 			/* Advance by data length. */
297 			fw_job.buf += evt_length;
298 			fw_job.len -= evt_length;
299 		}
300 	}
301 
302 	return (activate_patch);
303 }
304 
305 #define	IWMBT_SEND_FRAGMENT(fragment_type, size, msg)	do {		\
306 	iwmbt_debug("transferring %d bytes, offset %d", size, sent);	\
307 									\
308 	ret = iwmbt_send_fragment(hdl,					\
309 	    fragment_type,						\
310 	    fw->buf + sent,						\
311 	    XMIN(size, fw->len - sent),					\
312 	    IWMBT_HCI_CMD_TIMEOUT);					\
313 									\
314 	if (ret < 0) {							\
315 		iwmbt_debug("Failed to send "msg": code=%d", ret);	\
316 		return (-1);						\
317 	}								\
318 	sent += size;							\
319 } while (0)
320 
321 int
322 iwmbt_load_rsa_header(struct libusb_device_handle *hdl,
323     const struct iwmbt_firmware *fw)
324 {
325 	int ret, sent = 0;
326 
327 	IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
328 	IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1");
329 	IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 2");
330 
331 	/* skip 4 bytes */
332 	sent += 4;
333 
334 	IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1");
335 	IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2");
336 
337 	return (0);
338 }
339 
340 int
341 iwmbt_load_ecdsa_header(struct libusb_device_handle *hdl,
342     const struct iwmbt_firmware *fw)
343 {
344 	int ret, sent = ECDSA_OFFSET;
345 
346 	IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
347 	IWMBT_SEND_FRAGMENT(0x03, 0x60, "public key");
348 	IWMBT_SEND_FRAGMENT(0x02, 0x60, "signature");
349 
350 	return (0);
351 }
352 
353 int
354 iwmbt_load_fwfile(struct libusb_device_handle *hdl,
355     const struct iwmbt_firmware *fw, uint32_t *boot_param, int offset)
356 {
357 	int ready = 0, sent = offset;
358 	int ret, transferred;
359 	struct iwmbt_hci_cmd *cmd;
360 	struct iwmbt_hci_event *event;
361 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
362 
363 	/*
364 	 * Send firmware chunks. Chunk len must be 4 byte aligned.
365 	 * multiple commands can be combined
366 	 */
367 	while (fw->len - sent - ready >= (int) sizeof(struct iwmbt_hci_cmd)) {
368 		cmd = (struct iwmbt_hci_cmd *)(fw->buf + sent + ready);
369 		/* Parse firmware for Intel Reset HCI command parameter */
370 		if (cmd->opcode == htole16(0xfc0e)) {
371 			*boot_param = le32dec(cmd->data);
372 			iwmbt_debug("boot_param=0x%08x", *boot_param);
373 		}
374 		ready += IWMBT_HCI_CMD_SIZE(cmd);
375 		while (ready >= 0xFC) {
376 			IWMBT_SEND_FRAGMENT(0x01, 0xFC, "firmware chunk");
377 			ready -= 0xFC;
378 		}
379 		if (ready > 0 && ready % 4 == 0) {
380 			IWMBT_SEND_FRAGMENT(0x01, ready, "firmware chunk");
381 			ready = 0;
382 		}
383 	}
384 
385 	/* Wait for firmware download completion event */
386 	ret = libusb_interrupt_transfer(hdl,
387 	    IWMBT_INTERRUPT_ENDPOINT_ADDR,
388 	    buf,
389 	    sizeof(buf),
390 	    &transferred,
391 	    IWMBT_LOADCMPL_TIMEOUT);
392 
393 	if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
394 		iwmbt_err("libusb_interrupt_transfer() failed: "
395 		    "err=%s, size=%d",
396 		    libusb_strerror(ret),
397 		    transferred);
398 		return (-1);
399 	}
400 
401 	/* Expect Vendor Specific Event 0x06 */
402 	event = (struct iwmbt_hci_event *)buf;
403 	if (event->header.event != 0xFF || event->data[0] != 0x06) {
404 		iwmbt_err("firmware download completion event missed");
405 		return (-1);
406 	}
407 
408 	return (0);
409 }
410 
411 int
412 iwmbt_enter_manufacturer(struct libusb_device_handle *hdl)
413 {
414 	int ret, transferred;
415 	static struct iwmbt_hci_cmd cmd = {
416 		.opcode = htole16(0xfc11),
417 		.length = 2,
418 		.data = { 0x01, 0x00 },
419 	};
420 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
421 
422 	ret = iwmbt_hci_command(hdl,
423 	    &cmd,
424 	    buf,
425 	    sizeof(buf),
426 	    &transferred,
427 	    IWMBT_HCI_CMD_TIMEOUT);
428 
429 	if (ret < 0) {
430 		 iwmbt_debug("Can't enter manufacturer mode: code=%d, size=%d",
431 		     ret,
432 		     transferred);
433 		 return (-1);
434 	}
435 
436 	return (0);
437 }
438 
439 int
440 iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, int mode)
441 {
442 	int ret, transferred;
443 	static struct iwmbt_hci_cmd cmd = {
444 		.opcode = htole16(0xfc11),
445 		.length = 2,
446 		.data = { 0x00, 0x00 },
447 	};
448 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
449 
450 	/*
451 	 * The mode sets the type of reset we want to perform:
452 	 * 0x00: simply exit manufacturer mode without a reset.
453 	 * 0x01: exit manufacturer mode with a reset and patches disabled
454 	 * 0x02: exit manufacturer mode with a reset and patches enabled
455 	 */
456 	if (mode > 2) {
457 		iwmbt_debug("iwmbt_exit_manufacturer(): unknown mode (%d)",
458 				mode);
459 	}
460 	cmd.data[1] = mode;
461 
462 	ret = iwmbt_hci_command(hdl,
463 	    &cmd,
464 	    buf,
465 	    sizeof(buf),
466 	    &transferred,
467 	    IWMBT_HCI_CMD_TIMEOUT);
468 
469 	if (ret < 0) {
470 		 iwmbt_debug("Can't exit manufacturer mode: code=%d, size=%d",
471 		     ret,
472 		     transferred);
473 		 return (-1);
474 	}
475 
476 	return (0);
477 }
478 
479 int
480 iwmbt_get_version(struct libusb_device_handle *hdl,
481     struct iwmbt_version *version)
482 {
483 	int ret, transferred;
484 	struct iwmbt_hci_event_cmd_compl*event;
485 	struct iwmbt_hci_cmd cmd = {
486 		.opcode = htole16(0xfc05),
487 		.length = 0,
488 	};
489 	uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)];
490 
491 	memset(buf, 0, sizeof(buf));
492 
493 	ret = iwmbt_hci_command(hdl,
494 	    &cmd,
495 	    buf,
496 	    sizeof(buf),
497 	    &transferred,
498 	    IWMBT_HCI_CMD_TIMEOUT);
499 
500 	if (ret < 0 || transferred != sizeof(buf)) {
501 		 iwmbt_debug("Can't get version: : code=%d, size=%d",
502 		     ret,
503 		     transferred);
504 		 return (-1);
505 	}
506 
507 	event = (struct iwmbt_hci_event_cmd_compl *)buf;
508 	memcpy(version, event->data, sizeof(struct iwmbt_version));
509 
510 	return (0);
511 }
512 
513 int
514 iwmbt_get_version_tlv(struct libusb_device_handle *hdl,
515     struct iwmbt_version_tlv *version)
516 {
517 	int ret, transferred;
518 	struct iwmbt_hci_event_cmd_compl *event;
519 	static struct iwmbt_hci_cmd cmd = {
520 		.opcode = htole16(0xfc05),
521 		.length = 1,
522 		.data = { 0xff },
523 	};
524 	uint8_t status, datalen, type, len;
525 	uint8_t *data;
526 	uint8_t buf[255];
527 
528 	memset(buf, 0, sizeof(buf));
529 
530 	ret = iwmbt_hci_command(hdl,
531 	    &cmd,
532 	    buf,
533 	    sizeof(buf),
534 	    &transferred,
535 	    IWMBT_HCI_CMD_TIMEOUT);
536 
537 	if (ret < 0 || transferred < (int)IWMBT_HCI_EVT_COMPL_SIZE(uint16_t)) {
538 		 iwmbt_debug("Can't get version: code=%d, size=%d",
539 		     ret,
540 		     transferred);
541 		 return (-1);
542 	}
543 
544 	event = (struct iwmbt_hci_event_cmd_compl *)buf;
545 	memcpy(version, event->data, sizeof(struct iwmbt_version));
546 
547 	datalen = event->header.length - IWMBT_HCI_EVENT_COMPL_HEAD_SIZE;
548 	data = event->data;
549 	status = *data++;
550 	if (status != 0)
551 		return (-1);
552 	datalen--;
553 
554 	while (datalen >= 2) {
555 		type = *data++;
556 		len = *data++;
557 		datalen -= 2;
558 
559 		if (datalen < len)
560 			return (-1);
561 
562 		switch (type) {
563 		case IWMBT_TLV_CNVI_TOP:
564 			assert(len == 4);
565 			version->cnvi_top = le32dec(data);
566 			break;
567 		case IWMBT_TLV_CNVR_TOP:
568 			assert(len == 4);
569 			version->cnvr_top = le32dec(data);
570 			break;
571 		case IWMBT_TLV_CNVI_BT:
572 			assert(len == 4);
573 			version->cnvi_bt = le32dec(data);
574 			break;
575 		case IWMBT_TLV_CNVR_BT:
576 			assert(len == 4);
577 			version->cnvr_bt = le32dec(data);
578 			break;
579 		case IWMBT_TLV_DEV_REV_ID:
580 			assert(len == 2);
581 			version->dev_rev_id = le16dec(data);
582 			break;
583 		case IWMBT_TLV_IMAGE_TYPE:
584 			assert(len == 1);
585 			version->img_type = *data;
586 			break;
587 		case IWMBT_TLV_TIME_STAMP:
588 			assert(len == 2);
589 			version->min_fw_build_cw = data[0];
590 			version->min_fw_build_yy = data[1];
591 			version->timestamp = le16dec(data);
592 			break;
593 		case IWMBT_TLV_BUILD_TYPE:
594 			assert(len == 1);
595 			version->build_type = *data;
596 			break;
597 		case IWMBT_TLV_BUILD_NUM:
598 			assert(len == 4);
599 			version->min_fw_build_nn = *data;
600 			version->build_num = le32dec(data);
601 			break;
602 		case IWMBT_TLV_SECURE_BOOT:
603 			assert(len == 1);
604 			version->secure_boot = *data;
605 			break;
606 		case IWMBT_TLV_OTP_LOCK:
607 			assert(len == 1);
608 			version->otp_lock = *data;
609 			break;
610 		case IWMBT_TLV_API_LOCK:
611 			assert(len == 1);
612 			version->api_lock = *data;
613 			break;
614 		case IWMBT_TLV_DEBUG_LOCK:
615 			assert(len == 1);
616 			version->debug_lock = *data;
617 			break;
618 		case IWMBT_TLV_MIN_FW:
619 			assert(len == 3);
620 			version->min_fw_build_nn = data[0];
621 			version->min_fw_build_cw = data[1];
622 			version->min_fw_build_yy = data[2];
623 			break;
624 		case IWMBT_TLV_LIMITED_CCE:
625 			assert(len == 1);
626 			version->limited_cce = *data;
627 			break;
628 		case IWMBT_TLV_SBE_TYPE:
629 			assert(len == 1);
630 			version->sbe_type = *data;
631 			break;
632 		case IWMBT_TLV_OTP_BDADDR:
633 			memcpy(&version->otp_bd_addr, data, sizeof(bdaddr_t));
634 			break;
635 		default:
636 			/* Ignore other types */
637 			break;
638 		}
639 
640 		datalen -= len;
641 		data += len;
642 	}
643 
644 	return (0);
645 }
646 
647 int
648 iwmbt_get_boot_params(struct libusb_device_handle *hdl,
649     struct iwmbt_boot_params *params)
650 {
651 	int ret, transferred = 0;
652 	struct iwmbt_hci_event_cmd_compl *event;
653 	struct iwmbt_hci_cmd cmd = {
654 		.opcode = htole16(0xfc0d),
655 		.length = 0,
656 	};
657 	uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)];
658 
659 	memset(buf, 0, sizeof(buf));
660 
661 	ret = iwmbt_hci_command(hdl,
662 	    &cmd,
663 	    buf,
664 	    sizeof(buf),
665 	    &transferred,
666 	    IWMBT_HCI_CMD_TIMEOUT);
667 
668 	if (ret < 0 || transferred != sizeof(buf)) {
669 		 iwmbt_debug("Can't get boot params: code=%d, size=%d",
670 		     ret,
671 		     transferred);
672 		 return (-1);
673 	}
674 
675 	event = (struct iwmbt_hci_event_cmd_compl *)buf;
676 	memcpy(params, event->data, sizeof(struct iwmbt_boot_params));
677 
678 	return (0);
679 }
680 
681 int
682 iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param)
683 {
684 	int ret, transferred = 0;
685 	struct iwmbt_hci_event *event;
686 	static struct iwmbt_hci_cmd cmd = {
687 		.opcode = htole16(0xfc01),
688 		.length = 8,
689 		.data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 },
690 	};
691 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
692 
693 	le32enc(cmd.data + 4, boot_param);
694 	memset(buf, 0, sizeof(buf));
695 
696 	ret = iwmbt_hci_command(hdl,
697 	    &cmd,
698 	    buf,
699 	    sizeof(buf),
700 	    &transferred,
701 	    IWMBT_HCI_CMD_TIMEOUT);
702 
703 	if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
704 		 iwmbt_debug("Intel Reset command failed: code=%d, size=%d",
705 		    ret,
706 		    transferred);
707 		 return (ret);
708 	}
709 
710 	/* expect Vendor Specific Event 0x02 */
711 	event = (struct iwmbt_hci_event *)buf;
712 	if (event->header.event != 0xFF || event->data[0] != 0x02) {
713 		iwmbt_err("Intel Reset completion event missed");
714 		return (-1);
715 	}
716 
717 	return (0);
718 }
719 
720 int
721 iwmbt_load_ddc(struct libusb_device_handle *hdl,
722     const struct iwmbt_firmware *ddc)
723 {
724 	int size, sent = 0;
725 	int ret, transferred;
726 	uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
727 	uint8_t evt[IWMBT_HCI_MAX_CMD_SIZE];
728 	struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
729 
730 	size = ddc->len;
731 
732 	iwmbt_debug("file=%s, size=%d", ddc->fwname, size);
733 
734 	while (size > 0) {
735 
736 		memset(buf, 0, sizeof(buf));
737 		cmd->opcode = htole16(0xfc8b);
738 		cmd->length = ddc->buf[sent] + 1;
739 		memcpy(cmd->data, ddc->buf + sent, XMIN(ddc->buf[sent], size));
740 
741 		iwmbt_debug("transferring %d bytes, offset %d",
742 		    cmd->length,
743 		    sent);
744 
745 		size -= cmd->length;
746 		sent += cmd->length;
747 
748 		ret = iwmbt_hci_command(hdl,
749 		    cmd,
750 		    evt,
751 		    sizeof(evt),
752 		    &transferred,
753 		    IWMBT_HCI_CMD_TIMEOUT);
754 
755 		if (ret < 0) {
756 			 iwmbt_debug("Intel Write DDC failed: code=%d", ret);
757 			 return (-1);
758 		}
759 	}
760 
761 	return (0);
762 }
763 
764 int
765 iwmbt_set_event_mask(struct libusb_device_handle *hdl)
766 {
767 	int ret, transferred = 0;
768 	static struct iwmbt_hci_cmd cmd = {
769 		.opcode = htole16(0xfc52),
770 		.length = 8,
771 		.data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
772 	};
773 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
774 
775 	ret = iwmbt_hci_command(hdl,
776 	    &cmd,
777 	    buf,
778 	    sizeof(buf),
779 	    &transferred,
780 	    IWMBT_HCI_CMD_TIMEOUT);
781 
782 	if (ret < 0)
783 		 iwmbt_debug("Intel Set Event Mask failed: code=%d", ret);
784 
785 	return (ret);
786 }
787