]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
nvi: import version 2.2.1
[FreeBSD/FreeBSD.git] / usr.sbin / bluetooth / iwmbtfw / iwmbt_hw.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/param.h>
29 #include <sys/endian.h>
30 #include <sys/stat.h>
31
32 #include <err.h>
33 #include <errno.h>
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #include <libusb.h>
41
42 #include "iwmbt_fw.h"
43 #include "iwmbt_hw.h"
44 #include "iwmbt_dbg.h"
45
46 #define XMIN(x, y)      ((x) < (y) ? (x) : (y))
47
48 static int
49 iwmbt_send_fragment(struct libusb_device_handle *hdl,
50     uint8_t fragment_type, const void *data, uint8_t len, int timeout)
51 {
52         int ret, transferred;
53         uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
54         struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *) buf;
55
56         memset(buf, 0, sizeof(buf));
57         cmd->opcode = htole16(0xfc09),
58         cmd->length = len + 1,
59         cmd->data[0] = fragment_type;
60         memcpy(cmd->data + 1, data, len);
61
62         ret = libusb_bulk_transfer(hdl,
63             IWMBT_BULK_OUT_ENDPOINT_ADDR,
64             (uint8_t *)cmd,
65             IWMBT_HCI_CMD_SIZE(cmd),
66             &transferred,
67             timeout);
68
69         if (ret < 0 || transferred != (int)IWMBT_HCI_CMD_SIZE(cmd)) {
70                 iwmbt_err("libusb_bulk_transfer() failed: err=%s, size=%zu",
71                     libusb_strerror(ret),
72                     IWMBT_HCI_CMD_SIZE(cmd));
73                 return (-1);
74         }
75
76         ret = libusb_bulk_transfer(hdl,
77             IWMBT_BULK_IN_ENDPOINT_ADDR,
78             buf,
79             sizeof(buf),
80             &transferred,
81             timeout);
82
83         if (ret < 0) {
84                 iwmbt_err("libusb_bulk_transfer() failed: err=%s",
85                     libusb_strerror(ret));
86                 return (-1);
87         }
88
89         return (0);
90 }
91
92 static int
93 iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
94     void *event, int size, int *transferred, int timeout)
95 {
96         int ret;
97
98         ret = libusb_control_transfer(hdl,
99             LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
100             0,
101             0,
102             0,
103             (uint8_t *)cmd,
104             IWMBT_HCI_CMD_SIZE(cmd),
105             timeout);
106
107         if (ret < 0) {
108                 iwmbt_err("libusb_control_transfer() failed: err=%s",
109                     libusb_strerror(ret));
110                 return (ret);
111         }
112
113         ret = libusb_interrupt_transfer(hdl,
114             IWMBT_INTERRUPT_ENDPOINT_ADDR,
115             event,
116             size,
117             transferred,
118             timeout);
119
120         if (ret < 0)
121                 iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
122                     libusb_strerror(ret));
123
124         return (ret);
125 }
126
127 int
128 iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
129     const struct iwmbt_firmware *fw)
130 {
131         int ret, transferred;
132         struct iwmbt_firmware fw_job = *fw;
133         uint16_t cmd_opcode;
134         uint8_t cmd_length;
135         struct iwmbt_hci_cmd *cmd_buf;
136         uint8_t evt_code;
137         uint8_t evt_length;
138         uint8_t evt_buf[IWMBT_HCI_MAX_EVENT_SIZE];
139         int activate_patch = 0;
140
141         while (fw_job.len > 0) {
142                 if (fw_job.len < 4) {
143                         iwmbt_err("Invalid firmware, unexpected EOF in HCI "
144                             "command header. Remains=%d", fw_job.len);
145                         return (-1);
146                 }
147
148                 if (fw_job.buf[0] != 0x01) {
149                         iwmbt_err("Invalid firmware, expected HCI command (%d)",
150                                         fw_job.buf[0]);
151                         return (-1);
152                 }
153
154                 /* Advance by one. */
155                 fw_job.buf++;
156                 fw_job.len--;
157
158                 /* Load in the HCI command to perform. */
159                 cmd_opcode = le16dec(fw_job.buf);
160                 cmd_length = fw_job.buf[2];
161                 cmd_buf = (struct iwmbt_hci_cmd *)fw_job.buf;
162
163                 iwmbt_debug("opcode=%04x, len=%02x", cmd_opcode, cmd_length);
164
165                 /*
166                  * If there is a command that loads a patch in the
167                  * firmware file, then activate the patch upon success,
168                  * otherwise just disable the manufacturer mode.
169                  */
170                 if (cmd_opcode == 0xfc8e)
171                         activate_patch = 1;
172
173                 /* Advance by three. */
174                 fw_job.buf += 3;
175                 fw_job.len -= 3;
176
177                 if (fw_job.len < cmd_length) {
178                         iwmbt_err("Invalid firmware, unexpected EOF in HCI "
179                             "command data. len=%d, remains=%d",
180                             cmd_length, fw_job.len);
181                         return (-1);
182                 }
183
184                 /* Advance by data length. */
185                 fw_job.buf += cmd_length;
186                 fw_job.len -= cmd_length;
187
188                 ret = libusb_control_transfer(hdl,
189                     LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
190                     0,
191                     0,
192                     0,
193                     (uint8_t *)cmd_buf,
194                     IWMBT_HCI_CMD_SIZE(cmd_buf),
195                     IWMBT_HCI_CMD_TIMEOUT);
196
197                 if (ret < 0) {
198                         iwmbt_err("libusb_control_transfer() failed: err=%s",
199                             libusb_strerror(ret));
200                         return (-1);
201                 }
202
203                 /*
204                  * Every command has its associated event: data must match
205                  * what is recorded in the firmware file. Perform that check
206                  * now.
207                  */
208
209                 while (fw_job.len > 0 && fw_job.buf[0] == 0x02) {
210                         /* Is this the end of the file? */
211                         if (fw_job.len < 3) {
212                                 iwmbt_err("Invalid firmware, unexpected EOF in"
213                                     "event header. remains=%d", fw_job.len);
214                                 return (-1);
215                         }
216
217                         /* Advance by one. */
218                         fw_job.buf++;
219                         fw_job.len--;
220
221                         /* Load in the HCI event. */
222                         evt_code = fw_job.buf[0];
223                         evt_length = fw_job.buf[1];
224
225                         /* Advance by two. */
226                         fw_job.buf += 2;
227                         fw_job.len -= 2;
228
229                         /* Prepare HCI event buffer. */
230                         memset(evt_buf, 0, IWMBT_HCI_MAX_EVENT_SIZE);
231
232                         iwmbt_debug("event=%04x, len=%02x",
233                                         evt_code, evt_length);
234
235                         if (fw_job.len < evt_length) {
236                                 iwmbt_err("Invalid firmware, unexpected EOF in"
237                                     " event data. len=%d, remains=%d",
238                                     evt_length, fw_job.len);
239                                 return (-1);
240                         }
241
242                         ret = libusb_interrupt_transfer(hdl,
243                             IWMBT_INTERRUPT_ENDPOINT_ADDR,
244                             evt_buf,
245                             IWMBT_HCI_MAX_EVENT_SIZE,
246                             &transferred,
247                             IWMBT_HCI_CMD_TIMEOUT);
248
249                         if (ret < 0) {
250                                 iwmbt_err("libusb_interrupt_transfer() failed:"
251                                     " err=%s", libusb_strerror(ret));
252                                 return (-1);
253                         }
254
255                         if ((int)evt_length + 2 != transferred ||
256                             memcmp(evt_buf + 2, fw_job.buf, evt_length) != 0) {
257                                 iwmbt_err("event does not match firmware");
258                                 return (-1);
259                         }
260
261                         /* Advance by data length. */
262                         fw_job.buf += evt_length;
263                         fw_job.len -= evt_length;
264                 }
265         }
266
267         return (activate_patch);
268 }
269
270 int
271 iwmbt_load_fwfile(struct libusb_device_handle *hdl,
272     const struct iwmbt_firmware *fw, uint32_t *boot_param)
273 {
274         int ready = 0, sent = 0;
275         int ret, transferred;
276         struct iwmbt_hci_cmd *cmd;
277         struct iwmbt_hci_event *event;
278         uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
279
280 #define IWMBT_SEND_FRAGMENT(fragment_type, size, msg)   do {            \
281         iwmbt_debug("transferring %d bytes, offset %d", size, sent);    \
282                                                                         \
283         ret = iwmbt_send_fragment(hdl,                                  \
284             fragment_type,                                              \
285             fw->buf + sent,                                             \
286             XMIN(size, fw->len - sent),                                 \
287             IWMBT_HCI_CMD_TIMEOUT);                                     \
288                                                                         \
289         if (ret < 0) {                                                  \
290                 iwmbt_debug("Failed to send "msg": code=%d", ret);      \
291                 return (-1);                                            \
292         }                                                               \
293         sent += size;                                                   \
294 } while (0)
295
296         if (fw->len < 644) {
297                 iwmbt_err("Invalid size of firmware file (%d)", fw->len);
298                 return (-1);
299         }
300
301         iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len);
302
303         IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
304         IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1");
305         IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 2");
306
307         /* skip 4 bytes */
308         sent += 4;
309
310         IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1");
311         IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2");
312
313         /*
314          * Send firmware chunks. Chunk len must be 4 byte aligned.
315          * multiple commands can be combined
316          */
317         while (fw->len - sent - ready >= (int) sizeof(struct iwmbt_hci_cmd)) {
318                 cmd = (struct iwmbt_hci_cmd *)(fw->buf + sent + ready);
319                 /* Parse firmware for Intel Reset HCI command parameter */
320                 if (cmd->opcode == htole16(0xfc0e)) {
321                         *boot_param = le32dec(cmd->data);
322                         iwmbt_debug("boot_param=0x%08x", *boot_param);
323                 }
324                 ready += IWMBT_HCI_CMD_SIZE(cmd);
325                 while (ready >= 0xFC) {
326                         IWMBT_SEND_FRAGMENT(0x01, 0xFC, "firmware chunk");
327                         ready -= 0xFC;
328                 }
329                 if (ready > 0 && ready % 4 == 0) {
330                         IWMBT_SEND_FRAGMENT(0x01, ready, "firmware chunk");
331                         ready = 0;
332                 }
333         }
334
335         /* Wait for firmware download completion event */
336         ret = libusb_interrupt_transfer(hdl,
337             IWMBT_INTERRUPT_ENDPOINT_ADDR,
338             buf,
339             sizeof(buf),
340             &transferred,
341             IWMBT_LOADCMPL_TIMEOUT);
342
343         if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
344                 iwmbt_err("libusb_interrupt_transfer() failed: "
345                     "err=%s, size=%d",
346                     libusb_strerror(ret),
347                     transferred);
348                 return (-1);
349         }
350
351         /* Expect Vendor Specific Event 0x06 */
352         event = (struct iwmbt_hci_event *)buf;
353         if (event->header.event != 0xFF || event->data[0] != 0x06) {
354                 iwmbt_err("firmware download completion event missed");
355                 return (-1);
356         }
357
358         return (0);
359 }
360
361 int
362 iwmbt_enter_manufacturer(struct libusb_device_handle *hdl)
363 {
364         int ret, transferred;
365         static struct iwmbt_hci_cmd cmd = {
366                 .opcode = htole16(0xfc11),
367                 .length = 2,
368                 .data = { 0x01, 0x00 },
369         };
370         uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
371
372         ret = iwmbt_hci_command(hdl,
373             &cmd,
374             buf,
375             sizeof(buf),
376             &transferred,
377             IWMBT_HCI_CMD_TIMEOUT);
378
379         if (ret < 0) {
380                  iwmbt_debug("Can't enter manufacturer mode: code=%d, size=%d",
381                      ret,
382                      transferred);
383                  return (-1);
384         }
385
386         return (0);
387 }
388
389 int
390 iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, int mode)
391 {
392         int ret, transferred;
393         static struct iwmbt_hci_cmd cmd = {
394                 .opcode = htole16(0xfc11),
395                 .length = 2,
396                 .data = { 0x00, 0x00 },
397         };
398         uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
399
400         /*
401          * The mode sets the type of reset we want to perform:
402          * 0x00: simply exit manufacturer mode without a reset.
403          * 0x01: exit manufacturer mode with a reset and patches disabled
404          * 0x02: exit manufacturer mode with a reset and patches enabled
405          */
406         if (mode > 2) {
407                 iwmbt_debug("iwmbt_exit_manufacturer(): unknown mode (%d)",
408                                 mode);
409         }
410         cmd.data[1] = mode;
411
412         ret = iwmbt_hci_command(hdl,
413             &cmd,
414             buf,
415             sizeof(buf),
416             &transferred,
417             IWMBT_HCI_CMD_TIMEOUT);
418
419         if (ret < 0) {
420                  iwmbt_debug("Can't exit manufacturer mode: code=%d, size=%d",
421                      ret,
422                      transferred);
423                  return (-1);
424         }
425
426         return (0);
427 }
428
429 int
430 iwmbt_get_version(struct libusb_device_handle *hdl,
431     struct iwmbt_version *version)
432 {
433         int ret, transferred;
434         struct iwmbt_hci_event_cmd_compl*event;
435         struct iwmbt_hci_cmd cmd = {
436                 .opcode = htole16(0xfc05),
437                 .length = 0,
438         };
439         uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)];
440
441         memset(buf, 0, sizeof(buf));
442
443         ret = iwmbt_hci_command(hdl,
444             &cmd,
445             buf,
446             sizeof(buf),
447             &transferred,
448             IWMBT_HCI_CMD_TIMEOUT);
449
450         if (ret < 0 || transferred != sizeof(buf)) {
451                  iwmbt_debug("Can't get version: : code=%d, size=%d",
452                      ret,
453                      transferred);
454                  return (-1);
455         }
456
457         event = (struct iwmbt_hci_event_cmd_compl *)buf;
458         memcpy(version, event->data, sizeof(struct iwmbt_version));
459
460         return (0);
461 }
462
463 int
464 iwmbt_get_boot_params(struct libusb_device_handle *hdl,
465     struct iwmbt_boot_params *params)
466 {
467         int ret, transferred = 0;
468         struct iwmbt_hci_event_cmd_compl *event;
469         struct iwmbt_hci_cmd cmd = {
470                 .opcode = htole16(0xfc0d),
471                 .length = 0,
472         };
473         uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)];
474
475         memset(buf, 0, sizeof(buf));
476
477         ret = iwmbt_hci_command(hdl,
478             &cmd,
479             buf,
480             sizeof(buf),
481             &transferred,
482             IWMBT_HCI_CMD_TIMEOUT);
483
484         if (ret < 0 || transferred != sizeof(buf)) {
485                  iwmbt_debug("Can't get boot params: code=%d, size=%d",
486                      ret,
487                      transferred);
488                  return (-1);
489         }
490
491         event = (struct iwmbt_hci_event_cmd_compl *)buf;
492         memcpy(params, event->data, sizeof(struct iwmbt_boot_params));
493
494         return (0);
495 }
496
497 int
498 iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param)
499 {
500         int ret, transferred = 0;
501         struct iwmbt_hci_event *event;
502         static struct iwmbt_hci_cmd cmd = {
503                 .opcode = htole16(0xfc01),
504                 .length = 8,
505                 .data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 },
506         };
507         uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
508
509         le32enc(cmd.data + 4, boot_param);
510         memset(buf, 0, sizeof(buf));
511
512         ret = iwmbt_hci_command(hdl,
513             &cmd,
514             buf,
515             sizeof(buf),
516             &transferred,
517             IWMBT_HCI_CMD_TIMEOUT);
518
519         if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
520                  iwmbt_debug("Intel Reset command failed: code=%d, size=%d",
521                     ret,
522                     transferred);
523                  return (ret);
524         }
525
526         /* expect Vendor Specific Event 0x02 */
527         event = (struct iwmbt_hci_event *)buf;
528         if (event->header.event != 0xFF || event->data[0] != 0x02) {
529                 iwmbt_err("Intel Reset completion event missed");
530                 return (-1);
531         }
532
533         return (0);
534 }
535
536 int
537 iwmbt_load_ddc(struct libusb_device_handle *hdl,
538     const struct iwmbt_firmware *ddc)
539 {
540         int size, sent = 0;
541         int ret, transferred;
542         uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
543         struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
544
545         size = ddc->len;
546
547         iwmbt_debug("file=%s, size=%d", ddc->fwname, size);
548
549         while (size > 0) {
550
551                 memset(buf, 0, sizeof(buf));
552                 cmd->opcode = htole16(0xfc8b);
553                 cmd->length = ddc->buf[sent] + 1;
554                 memcpy(cmd->data, ddc->buf + sent, XMIN(ddc->buf[sent], size));
555
556                 iwmbt_debug("transferring %d bytes, offset %d",
557                     cmd->length,
558                     sent);
559
560                 size -= cmd->length;
561                 sent += cmd->length;
562
563                 ret = iwmbt_hci_command(hdl,
564                     cmd,
565                     buf,
566                     sizeof(buf),
567                     &transferred,
568                     IWMBT_HCI_CMD_TIMEOUT);
569
570                 if (ret < 0) {
571                          iwmbt_debug("Intel Write DDC failed: code=%d", ret);
572                          return (-1);
573                 }
574         }
575
576         return (0);
577 }
578
579 int
580 iwmbt_set_event_mask(struct libusb_device_handle *hdl)
581 {
582         int ret, transferred = 0;
583         static struct iwmbt_hci_cmd cmd = {
584                 .opcode = htole16(0xfc52),
585                 .length = 8,
586                 .data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
587         };
588         uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
589
590         ret = iwmbt_hci_command(hdl,
591             &cmd,
592             buf,
593             sizeof(buf),
594             &transferred,
595             IWMBT_HCI_CMD_TIMEOUT);
596
597         if (ret < 0)
598                  iwmbt_debug("Intel Set Event Mask failed: code=%d", ret);
599
600         return (ret);
601 }