Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
108545 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2019 Vladimir Kondratyev <[email protected]>
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,
441
enum iwmbt_mm_exit mode)
442
{
443
int ret, transferred;
444
static struct iwmbt_hci_cmd cmd = {
445
.opcode = htole16(0xfc11),
446
.length = 2,
447
.data = { 0x00, 0x00 },
448
};
449
uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
450
451
cmd.data[1] = (uint8_t)mode;
452
453
ret = iwmbt_hci_command(hdl,
454
&cmd,
455
buf,
456
sizeof(buf),
457
&transferred,
458
IWMBT_HCI_CMD_TIMEOUT);
459
460
if (ret < 0) {
461
iwmbt_debug("Can't exit manufacturer mode: code=%d, size=%d",
462
ret,
463
transferred);
464
return (-1);
465
}
466
467
return (0);
468
}
469
470
int
471
iwmbt_get_version(struct libusb_device_handle *hdl,
472
struct iwmbt_version *version)
473
{
474
int ret, transferred;
475
struct iwmbt_hci_event_cmd_compl*event;
476
struct iwmbt_hci_cmd cmd = {
477
.opcode = htole16(0xfc05),
478
.length = 0,
479
};
480
uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)];
481
482
memset(buf, 0, sizeof(buf));
483
484
ret = iwmbt_hci_command(hdl,
485
&cmd,
486
buf,
487
sizeof(buf),
488
&transferred,
489
IWMBT_HCI_CMD_TIMEOUT);
490
491
if (ret < 0 || transferred != sizeof(buf)) {
492
iwmbt_debug("Can't get version: : code=%d, size=%d",
493
ret,
494
transferred);
495
return (-1);
496
}
497
498
event = (struct iwmbt_hci_event_cmd_compl *)buf;
499
memcpy(version, event->data, sizeof(struct iwmbt_version));
500
501
return (0);
502
}
503
504
int
505
iwmbt_get_version_tlv(struct libusb_device_handle *hdl,
506
struct iwmbt_version_tlv *version)
507
{
508
int ret, transferred;
509
struct iwmbt_hci_event_cmd_compl *event;
510
static struct iwmbt_hci_cmd cmd = {
511
.opcode = htole16(0xfc05),
512
.length = 1,
513
.data = { 0xff },
514
};
515
uint8_t status, datalen, type, len;
516
uint8_t *data;
517
uint8_t buf[255];
518
519
memset(buf, 0, sizeof(buf));
520
521
ret = iwmbt_hci_command(hdl,
522
&cmd,
523
buf,
524
sizeof(buf),
525
&transferred,
526
IWMBT_HCI_CMD_TIMEOUT);
527
528
if (ret < 0 || transferred < (int)IWMBT_HCI_EVT_COMPL_SIZE(uint16_t)) {
529
iwmbt_debug("Can't get version: code=%d, size=%d",
530
ret,
531
transferred);
532
return (-1);
533
}
534
535
event = (struct iwmbt_hci_event_cmd_compl *)buf;
536
memcpy(version, event->data, sizeof(struct iwmbt_version));
537
538
datalen = event->header.length - IWMBT_HCI_EVENT_COMPL_HEAD_SIZE;
539
data = event->data;
540
status = *data++;
541
if (status != 0)
542
return (-1);
543
datalen--;
544
545
while (datalen >= 2) {
546
type = *data++;
547
len = *data++;
548
datalen -= 2;
549
550
if (datalen < len)
551
return (-1);
552
553
switch (type) {
554
case IWMBT_TLV_CNVI_TOP:
555
assert(len == 4);
556
version->cnvi_top = le32dec(data);
557
break;
558
case IWMBT_TLV_CNVR_TOP:
559
assert(len == 4);
560
version->cnvr_top = le32dec(data);
561
break;
562
case IWMBT_TLV_CNVI_BT:
563
assert(len == 4);
564
version->cnvi_bt = le32dec(data);
565
break;
566
case IWMBT_TLV_CNVR_BT:
567
assert(len == 4);
568
version->cnvr_bt = le32dec(data);
569
break;
570
case IWMBT_TLV_DEV_REV_ID:
571
assert(len == 2);
572
version->dev_rev_id = le16dec(data);
573
break;
574
case IWMBT_TLV_IMAGE_TYPE:
575
assert(len == 1);
576
version->img_type = *data;
577
break;
578
case IWMBT_TLV_TIME_STAMP:
579
assert(len == 2);
580
version->min_fw_build_cw = data[0];
581
version->min_fw_build_yy = data[1];
582
version->timestamp = le16dec(data);
583
break;
584
case IWMBT_TLV_BUILD_TYPE:
585
assert(len == 1);
586
version->build_type = *data;
587
break;
588
case IWMBT_TLV_BUILD_NUM:
589
assert(len == 4);
590
version->min_fw_build_nn = *data;
591
version->build_num = le32dec(data);
592
break;
593
case IWMBT_TLV_SECURE_BOOT:
594
assert(len == 1);
595
version->secure_boot = *data;
596
break;
597
case IWMBT_TLV_OTP_LOCK:
598
assert(len == 1);
599
version->otp_lock = *data;
600
break;
601
case IWMBT_TLV_API_LOCK:
602
assert(len == 1);
603
version->api_lock = *data;
604
break;
605
case IWMBT_TLV_DEBUG_LOCK:
606
assert(len == 1);
607
version->debug_lock = *data;
608
break;
609
case IWMBT_TLV_MIN_FW:
610
assert(len == 3);
611
version->min_fw_build_nn = data[0];
612
version->min_fw_build_cw = data[1];
613
version->min_fw_build_yy = data[2];
614
break;
615
case IWMBT_TLV_LIMITED_CCE:
616
assert(len == 1);
617
version->limited_cce = *data;
618
break;
619
case IWMBT_TLV_SBE_TYPE:
620
assert(len == 1);
621
version->sbe_type = *data;
622
break;
623
case IWMBT_TLV_OTP_BDADDR:
624
memcpy(&version->otp_bd_addr, data, sizeof(bdaddr_t));
625
break;
626
default:
627
/* Ignore other types */
628
break;
629
}
630
631
datalen -= len;
632
data += len;
633
}
634
635
return (0);
636
}
637
638
int
639
iwmbt_get_boot_params(struct libusb_device_handle *hdl,
640
struct iwmbt_boot_params *params)
641
{
642
int ret, transferred = 0;
643
struct iwmbt_hci_event_cmd_compl *event;
644
struct iwmbt_hci_cmd cmd = {
645
.opcode = htole16(0xfc0d),
646
.length = 0,
647
};
648
uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)];
649
650
memset(buf, 0, sizeof(buf));
651
652
ret = iwmbt_hci_command(hdl,
653
&cmd,
654
buf,
655
sizeof(buf),
656
&transferred,
657
IWMBT_HCI_CMD_TIMEOUT);
658
659
if (ret < 0 || transferred != sizeof(buf)) {
660
iwmbt_debug("Can't get boot params: code=%d, size=%d",
661
ret,
662
transferred);
663
return (-1);
664
}
665
666
event = (struct iwmbt_hci_event_cmd_compl *)buf;
667
memcpy(params, event->data, sizeof(struct iwmbt_boot_params));
668
669
return (0);
670
}
671
672
int
673
iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param)
674
{
675
int ret, transferred = 0;
676
struct iwmbt_hci_event *event;
677
static struct iwmbt_hci_cmd cmd = {
678
.opcode = htole16(0xfc01),
679
.length = 8,
680
.data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 },
681
};
682
uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
683
684
le32enc(cmd.data + 4, boot_param);
685
memset(buf, 0, sizeof(buf));
686
687
ret = iwmbt_hci_command(hdl,
688
&cmd,
689
buf,
690
sizeof(buf),
691
&transferred,
692
IWMBT_HCI_CMD_TIMEOUT);
693
694
if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
695
iwmbt_debug("Intel Reset command failed: code=%d, size=%d",
696
ret,
697
transferred);
698
return (ret);
699
}
700
701
/* expect Vendor Specific Event 0x02 */
702
event = (struct iwmbt_hci_event *)buf;
703
if (event->header.event != 0xFF || event->data[0] != 0x02) {
704
iwmbt_err("Intel Reset completion event missed");
705
return (-1);
706
}
707
708
return (0);
709
}
710
711
int
712
iwmbt_load_ddc(struct libusb_device_handle *hdl,
713
const struct iwmbt_firmware *ddc)
714
{
715
int size, sent = 0;
716
int ret, transferred;
717
uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
718
uint8_t evt[IWMBT_HCI_MAX_CMD_SIZE];
719
struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
720
721
size = ddc->len;
722
723
iwmbt_debug("file=%s, size=%d", ddc->fwname, size);
724
725
while (size > 0) {
726
727
memset(buf, 0, sizeof(buf));
728
cmd->opcode = htole16(0xfc8b);
729
cmd->length = ddc->buf[sent] + 1;
730
memcpy(cmd->data, ddc->buf + sent, XMIN(ddc->buf[sent], size));
731
732
iwmbt_debug("transferring %d bytes, offset %d",
733
cmd->length,
734
sent);
735
736
size -= cmd->length;
737
sent += cmd->length;
738
739
ret = iwmbt_hci_command(hdl,
740
cmd,
741
evt,
742
sizeof(evt),
743
&transferred,
744
IWMBT_HCI_CMD_TIMEOUT);
745
746
if (ret < 0) {
747
iwmbt_debug("Intel Write DDC failed: code=%d", ret);
748
return (-1);
749
}
750
}
751
752
return (0);
753
}
754
755
int
756
iwmbt_set_event_mask(struct libusb_device_handle *hdl)
757
{
758
int ret, transferred = 0;
759
static struct iwmbt_hci_cmd cmd = {
760
.opcode = htole16(0xfc52),
761
.length = 8,
762
.data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
763
};
764
uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
765
766
ret = iwmbt_hci_command(hdl,
767
&cmd,
768
buf,
769
sizeof(buf),
770
&transferred,
771
IWMBT_HCI_CMD_TIMEOUT);
772
773
if (ret < 0)
774
iwmbt_debug("Intel Set Event Mask failed: code=%d", ret);
775
776
return (ret);
777
}
778
779