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
iwmbt_send_fragment(struct libusb_device_handle * hdl,uint8_t fragment_type,const void * data,uint8_t len,int timeout)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
iwmbt_hci_command(struct libusb_device_handle * hdl,struct iwmbt_hci_cmd * cmd,void * event,int size,int * transferred,int timeout)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
iwmbt_patch_fwfile(struct libusb_device_handle * hdl,const struct iwmbt_firmware * fw)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
iwmbt_load_rsa_header(struct libusb_device_handle * hdl,const struct iwmbt_firmware * fw)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
iwmbt_load_ecdsa_header(struct libusb_device_handle * hdl,const struct iwmbt_firmware * fw)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
iwmbt_load_fwfile(struct libusb_device_handle * hdl,const struct iwmbt_firmware * fw,uint32_t * boot_param,int offset)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
iwmbt_enter_manufacturer(struct libusb_device_handle * hdl)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
iwmbt_exit_manufacturer(struct libusb_device_handle * hdl,int mode)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
iwmbt_get_version(struct libusb_device_handle * hdl,struct iwmbt_version * version)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
iwmbt_get_version_tlv(struct libusb_device_handle * hdl,struct iwmbt_version_tlv * version)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
iwmbt_get_boot_params(struct libusb_device_handle * hdl,struct iwmbt_boot_params * params)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
iwmbt_intel_reset(struct libusb_device_handle * hdl,uint32_t boot_param)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
iwmbt_load_ddc(struct libusb_device_handle * hdl,const struct iwmbt_firmware * ddc)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
iwmbt_set_event_mask(struct libusb_device_handle * hdl)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