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
50static int
51iwmbt_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
94static int
95iwmbt_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
129int
130iwmbt_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
272int
273iwmbt_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
363int
364iwmbt_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
391int
392iwmbt_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
431int
432iwmbt_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
465int
466iwmbt_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
499int
500iwmbt_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
538int
539iwmbt_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
581int
582iwmbt_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}
604