]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
ident(1): Normalizing date format
[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/types.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_load_fwfile(struct libusb_device_handle *hdl,
131     const struct iwmbt_firmware *fw, uint32_t *boot_param)
132 {
133         int ready = 0, sent = 0;
134         int ret, transferred;
135         struct iwmbt_hci_cmd *cmd;
136         struct iwmbt_hci_event *event;
137         uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
138
139 #define IWMBT_SEND_FRAGMENT(fragment_type, size, msg)   do {            \
140         iwmbt_debug("transferring %d bytes, offset %d", size, sent);    \
141                                                                         \
142         ret = iwmbt_send_fragment(hdl,                                  \
143             fragment_type,                                              \
144             fw->buf + sent,                                             \
145             XMIN(size, fw->len - sent),                                 \
146             IWMBT_HCI_CMD_TIMEOUT);                                     \
147                                                                         \
148         if (ret < 0) {                                                  \
149                 iwmbt_debug("Failed to send "msg": code=%d", ret);      \
150                 return (-1);                                            \
151         }                                                               \
152         sent += size;                                                   \
153 } while (0)
154
155         if (fw->len < 644) {
156                 iwmbt_err("Invalid size of firmware file (%d)", fw->len);
157                 return (-1);
158         }
159
160         iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len);
161
162         IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
163         IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1");
164         IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 2");
165
166         /* skip 4 bytes */
167         sent += 4;
168
169         IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1");
170         IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2");
171
172         /*
173          * Send firmware chunks. Chunk len must be 4 byte aligned.
174          * multiple commands can be combined
175          */
176         while (fw->len - sent - ready >= (int) sizeof(struct iwmbt_hci_cmd)) {
177                 cmd = (struct iwmbt_hci_cmd *)(fw->buf + sent + ready);
178                 /* Parse firmware for Intel Reset HCI command parameter */
179                 if (cmd->opcode == htole16(0xfc0e)) {
180                         *boot_param = le32dec(cmd->data);
181                         iwmbt_debug("boot_param=0x%08x", *boot_param);
182                 }
183                 ready += IWMBT_HCI_CMD_SIZE(cmd);
184                 while (ready >= 0xFC) {
185                         IWMBT_SEND_FRAGMENT(0x01, 0xFC, "firmware chunk");
186                         ready -= 0xFC;
187                 }
188                 if (ready > 0 && ready % 4 == 0) {
189                         IWMBT_SEND_FRAGMENT(0x01, ready, "firmware chunk");
190                         ready = 0;
191                 }
192         }
193
194         /* Wait for firmware download completion event */
195         ret = libusb_interrupt_transfer(hdl,
196             IWMBT_INTERRUPT_ENDPOINT_ADDR,
197             buf,
198             sizeof(buf),
199             &transferred,
200             IWMBT_LOADCMPL_TIMEOUT);
201
202         if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
203                 iwmbt_err("libusb_interrupt_transfer() failed: "
204                     "err=%s, size=%d",
205                     libusb_strerror(ret),
206                     transferred);
207                 return (-1);
208         }
209
210         /* Expect Vendor Specific Event 0x06 */
211         event = (struct iwmbt_hci_event *)buf;
212         if (event->header.event != 0xFF || event->data[0] != 0x06) {
213                 iwmbt_err("firmware download completion event missed");
214                 return (-1);
215         }
216
217         return (0);
218 }
219
220 int
221 iwmbt_get_version(struct libusb_device_handle *hdl,
222     struct iwmbt_version *version)
223 {
224         int ret, transferred;
225         struct iwmbt_hci_event_cmd_compl*event;
226         struct iwmbt_hci_cmd cmd = {
227                 .opcode = htole16(0xfc05),
228                 .length = 0,
229         };
230         uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)];
231
232         memset(buf, 0, sizeof(buf));
233
234         ret = iwmbt_hci_command(hdl,
235             &cmd,
236             buf,
237             sizeof(buf),
238             &transferred,
239             IWMBT_HCI_CMD_TIMEOUT);
240
241         if (ret < 0 || transferred != sizeof(buf)) {
242                  iwmbt_debug("Can't get version: : code=%d, size=%d",
243                      ret,
244                      transferred);
245                  return (-1);
246         }
247
248         event = (struct iwmbt_hci_event_cmd_compl *)buf;
249         memcpy(version, event->data, sizeof(struct iwmbt_version));
250
251         return (0);
252 }
253
254 int
255 iwmbt_get_boot_params(struct libusb_device_handle *hdl,
256     struct iwmbt_boot_params *params)
257 {
258         int ret, transferred = 0;
259         struct iwmbt_hci_event_cmd_compl *event;
260         struct iwmbt_hci_cmd cmd = {
261                 .opcode = htole16(0xfc0d),
262                 .length = 0,
263         };
264         uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)];
265
266         memset(buf, 0, sizeof(buf));
267
268         ret = iwmbt_hci_command(hdl,
269             &cmd,
270             buf,
271             sizeof(buf),
272             &transferred,
273             IWMBT_HCI_CMD_TIMEOUT);
274
275         if (ret < 0 || transferred != sizeof(buf)) {
276                  iwmbt_debug("Can't get boot params: code=%d, size=%d",
277                      ret,
278                      transferred);
279                  return (-1);
280         }
281
282         event = (struct iwmbt_hci_event_cmd_compl *)buf;
283         memcpy(params, event->data, sizeof(struct iwmbt_boot_params));
284
285         return (0);
286 }
287
288 int
289 iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param)
290 {
291         int ret, transferred = 0;
292         struct iwmbt_hci_event *event;
293         static struct iwmbt_hci_cmd cmd = {
294                 .opcode = htole16(0xfc01),
295                 .length = 8,
296                 .data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 },
297         };
298         uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
299
300         le32enc(cmd.data + 4, boot_param);
301         memset(buf, 0, sizeof(buf));
302
303         ret = iwmbt_hci_command(hdl,
304             &cmd,
305             buf,
306             sizeof(buf),
307             &transferred,
308             IWMBT_HCI_CMD_TIMEOUT);
309
310         if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
311                  iwmbt_debug("Intel Reset command failed: code=%d, size=%d",
312                     ret,
313                     transferred);
314                  return (ret);
315         }
316
317         /* expect Vendor Specific Event 0x02 */
318         event = (struct iwmbt_hci_event *)buf;
319         if (event->header.event != 0xFF || event->data[0] != 0x02) {
320                 iwmbt_err("Intel Reset completion event missed");
321                 return (-1);
322         }
323
324         return (0);
325 }
326
327 int
328 iwmbt_load_ddc(struct libusb_device_handle *hdl,
329     const struct iwmbt_firmware *ddc)
330 {
331         int size, sent = 0;
332         int ret, transferred;
333         uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
334         struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
335
336         size = ddc->len;
337
338         iwmbt_debug("file=%s, size=%d", ddc->fwname, size);
339
340         while (size > 0) {
341
342                 memset(buf, 0, sizeof(buf));
343                 cmd->opcode = htole16(0xfc8b);
344                 cmd->length = ddc->buf[sent] + 1;
345                 memcpy(cmd->data, ddc->buf + sent, XMIN(ddc->buf[sent], size));
346
347                 iwmbt_debug("transferring %d bytes, offset %d",
348                     cmd->length,
349                     sent);
350
351                 size -= cmd->length;
352                 sent += cmd->length;
353
354                 ret = iwmbt_hci_command(hdl,
355                     cmd,
356                     buf,
357                     sizeof(buf),
358                     &transferred,
359                     IWMBT_HCI_CMD_TIMEOUT);
360
361                 if (ret < 0) {
362                          iwmbt_debug("Intel Write DDC failed: code=%d", ret);
363                          return (-1);
364                 }
365         }
366
367         return (0);
368 }
369
370 int
371 iwmbt_set_event_mask(struct libusb_device_handle *hdl)
372 {
373         int ret, transferred = 0;
374         static struct iwmbt_hci_cmd cmd = {
375                 .opcode = htole16(0xfc52),
376                 .length = 8,
377                 .data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
378         };
379         uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
380
381         ret = iwmbt_hci_command(hdl,
382             &cmd,
383             buf,
384             sizeof(buf),
385             &transferred,
386             IWMBT_HCI_CMD_TIMEOUT);
387
388         if (ret < 0)
389                  iwmbt_debug("Intel Set Event Mask failed: code=%d", ret);
390
391         return (ret);
392 }