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