1/*
2 * Copyright (C) 2014 Andrew Duggan
3 * Copyright (C) 2014 Synaptics Inc
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <alloca.h>
19#include <time.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <unistd.h>
23#include <string.h>
24#include <stdlib.h>
25#include <errno.h>
26
27#include "rmi4update.h"
28
29#define RMI_F34_QUERY_SIZE		7
30#define RMI_F34_HAS_NEW_REG_MAP		(1 << 0)
31#define RMI_F34_IS_UNLOCKED		(1 << 1)
32#define RMI_F34_HAS_CONFIG_ID		(1 << 2)
33#define RMI_F34_BLOCK_SIZE_OFFSET	1
34#define RMI_F34_FW_BLOCKS_OFFSET	3
35#define RMI_F34_CONFIG_BLOCKS_OFFSET	5
36
37#define RMI_F34_BLOCK_SIZE_V1_OFFSET	0
38#define RMI_F34_FW_BLOCKS_V1_OFFSET	0
39#define RMI_F34_CONFIG_BLOCKS_V1_OFFSET	2
40
41#define RMI_F34_BLOCK_DATA_OFFSET	2
42#define RMI_F34_BLOCK_DATA_V1_OFFSET	1
43
44#define RMI_F34_COMMAND_MASK		0x0F
45#define RMI_F34_STATUS_MASK		0x07
46#define RMI_F34_STATUS_SHIFT		4
47#define RMI_F34_ENABLED_MASK		0x80
48
49#define RMI_F34_COMMAND_V1_MASK		0x3F
50#define RMI_F34_STATUS_V1_MASK		0x3F
51#define RMI_F34_ENABLED_V1_MASK		0x80
52
53#define RMI_F34_WRITE_FW_BLOCK        0x02
54#define RMI_F34_ERASE_ALL             0x03
55#define RMI_F34_WRITE_LOCKDOWN_BLOCK  0x04
56#define RMI_F34_WRITE_CONFIG_BLOCK    0x06
57#define RMI_F34_ENABLE_FLASH_PROG     0x0f
58
59#define RMI_F34_ENABLE_WAIT_MS 300
60#define RMI_F34_ERASE_WAIT_MS (5 * 1000)
61#define RMI_F34_IDLE_WAIT_MS 500
62
63/* Most recent device status event */
64#define RMI_F01_STATUS_CODE(status)		((status) & 0x0f)
65/* Indicates that flash programming is enabled (bootloader mode). */
66#define RMI_F01_STATUS_BOOTLOADER(status)	(!!((status) & 0x40))
67/* The device has lost its configuration for some reason. */
68#define RMI_F01_STATUS_UNCONFIGURED(status)	(!!((status) & 0x80))
69
70/*
71 * Sleep mode controls power management on the device and affects all
72 * functions of the device.
73 */
74#define RMI_F01_CTRL0_SLEEP_MODE_MASK	0x03
75
76#define RMI_SLEEP_MODE_NORMAL		0x00
77#define RMI_SLEEP_MODE_SENSOR_SLEEP	0x01
78#define RMI_SLEEP_MODE_RESERVED0	0x02
79#define RMI_SLEEP_MODE_RESERVED1	0x03
80
81/*
82 * This bit disables whatever sleep mode may be selected by the sleep_mode
83 * field and forces the device to run at full power without sleeping.
84 */
85#define RMI_F01_CRTL0_NOSLEEP_BIT	(1 << 2)
86
87int RMI4Update::UpdateFirmware(bool force, bool performLockdown)
88{
89	struct timespec start;
90	struct timespec end;
91	long long int duration_us = 0;
92	int rc;
93	const unsigned char eraseAll = RMI_F34_ERASE_ALL;
94
95	rc = FindUpdateFunctions();
96	if (rc != UPDATE_SUCCESS)
97		return rc;
98
99	rc = m_device.QueryBasicProperties();
100	if (rc < 0)
101		return UPDATE_FAIL_QUERY_BASIC_PROPERTIES;
102
103	if (!force && m_firmwareImage.HasIO()) {
104		if (m_firmwareImage.GetFirmwareID() <= m_device.GetFirmwareID()) {
105			fprintf(stderr, "Firmware image (%ld) is not newer then the firmware on the device (%ld)\n",
106				m_firmwareImage.GetFirmwareID(), m_device.GetFirmwareID());
107			rc = UPDATE_FAIL_FIRMWARE_IMAGE_IS_OLDER;
108			return rc;
109		}
110	}
111
112	fprintf(stdout, "Device Properties:\n");
113	m_device.PrintProperties();
114
115	rc = DisableNonessentialInterupts();
116	if (rc != UPDATE_SUCCESS)
117		return rc;
118
119	rc = ReadF34Queries();
120	if (rc != UPDATE_SUCCESS)
121		return rc;
122
123	rc = m_firmwareImage.VerifyImageMatchesDevice(GetFirmwareSize(), GetConfigSize());
124	if (rc != UPDATE_SUCCESS)
125		return rc;
126
127	rc = EnterFlashProgramming();
128	if (rc != UPDATE_SUCCESS) {
129		fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
130		goto reset;
131	}
132
133	if (performLockdown && m_unlocked) {
134		if (m_firmwareImage.GetLockdownData()) {
135			fprintf(stdout, "Writing lockdown...\n");
136			clock_gettime(CLOCK_MONOTONIC, &start);
137			rc = WriteBlocks(m_firmwareImage.GetLockdownData(),
138					m_firmwareImage.GetLockdownSize() / 0x10,
139					RMI_F34_WRITE_LOCKDOWN_BLOCK);
140			if (rc != UPDATE_SUCCESS) {
141				fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
142				goto reset;
143			}
144			clock_gettime(CLOCK_MONOTONIC, &end);
145			duration_us = diff_time(&start, &end);
146			fprintf(stdout, "Done writing lockdown, time: %lld us.\n", duration_us);
147		}
148
149		rc = EnterFlashProgramming();
150		if (rc != UPDATE_SUCCESS) {
151			fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
152			goto reset;
153		}
154
155	}
156
157	rc = WriteBootloaderID();
158	if (rc != UPDATE_SUCCESS) {
159		fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
160		goto reset;
161	}
162
163	fprintf(stdout, "Erasing FW...\n");
164	clock_gettime(CLOCK_MONOTONIC, &start);
165	rc = m_device.Write(m_f34StatusAddr, &eraseAll, 1);
166	if (rc != 1) {
167		fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(UPDATE_FAIL_ERASE_ALL));
168		rc = UPDATE_FAIL_ERASE_ALL;
169		goto reset;
170	}
171
172	rc = WaitForIdle(RMI_F34_ERASE_WAIT_MS);
173	if (rc != UPDATE_SUCCESS) {
174		fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
175		goto reset;
176	}
177	clock_gettime(CLOCK_MONOTONIC, &end);
178	duration_us = diff_time(&start, &end);
179	fprintf(stdout, "Erase complete, time: %lld us.\n", duration_us);
180
181	if (m_firmwareImage.GetFirmwareData()) {
182		fprintf(stdout, "Writing firmware...\n");
183		clock_gettime(CLOCK_MONOTONIC, &start);
184		rc = WriteBlocks(m_firmwareImage.GetFirmwareData(), m_fwBlockCount,
185						RMI_F34_WRITE_FW_BLOCK);
186		if (rc != UPDATE_SUCCESS) {
187			fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
188			goto reset;
189		}
190		clock_gettime(CLOCK_MONOTONIC, &end);
191		duration_us = diff_time(&start, &end);
192		fprintf(stdout, "Done writing FW, time: %lld us.\n", duration_us);
193	}
194
195	if (m_firmwareImage.GetConfigData()) {
196		fprintf(stdout, "Writing configuration...\n");
197		clock_gettime(CLOCK_MONOTONIC, &start);
198		rc = WriteBlocks(m_firmwareImage.GetConfigData(), m_configBlockCount,
199				RMI_F34_WRITE_CONFIG_BLOCK);
200		if (rc != UPDATE_SUCCESS) {
201			fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
202			goto reset;
203		}
204		clock_gettime(CLOCK_MONOTONIC, &end);
205		duration_us = diff_time(&start, &end);
206		fprintf(stdout, "Done writing config, time: %lld us.\n", duration_us);
207	}
208
209reset:
210	m_device.Reset();
211	m_device.RebindDriver();
212	return rc;
213
214}
215
216int RMI4Update::DisableNonessentialInterupts()
217{
218	int rc;
219	unsigned char interruptEnabeMask = m_f34.GetInterruptMask() | m_f01.GetInterruptMask();
220
221	rc = m_device.Write(m_f01.GetControlBase() + 1, &interruptEnabeMask, 1);
222	if (rc != 1)
223		return rc;
224
225	return UPDATE_SUCCESS;
226}
227
228int RMI4Update::FindUpdateFunctions()
229{
230	if (0 > m_device.ScanPDT())
231		return UPDATE_FAIL_SCAN_PDT;
232
233	if (!m_device.GetFunction(m_f01, 0x01))
234		return UPDATE_FAIL_NO_FUNCTION_01;
235
236	if (!m_device.GetFunction(m_f34, 0x34))
237		return UPDATE_FAIL_NO_FUNCTION_34;
238
239	return UPDATE_SUCCESS;
240}
241
242int RMI4Update::ReadF34Queries()
243{
244	int rc;
245	unsigned char idStr[3];
246	unsigned char buf[8];
247	unsigned short queryAddr = m_f34.GetQueryBase();
248	unsigned short f34Version = m_f34.GetFunctionVersion();
249	unsigned short querySize;
250
251	if (f34Version == 0x1)
252		querySize = 8;
253	else
254		querySize = 2;
255
256	rc = m_device.Read(queryAddr, m_bootloaderID, RMI_BOOTLOADER_ID_SIZE);
257	if (rc != RMI_BOOTLOADER_ID_SIZE)
258		return UPDATE_FAIL_READ_BOOTLOADER_ID;
259
260	if (f34Version == 0x1)
261		++queryAddr;
262	else
263		queryAddr += querySize;
264
265	if (f34Version == 0x1) {
266		rc = m_device.Read(queryAddr, buf, 1);
267		if (rc != 1)
268			return UPDATE_FAIL_READ_F34_QUERIES;
269
270		m_hasNewRegmap = buf[0] & RMI_F34_HAS_NEW_REG_MAP;
271		m_unlocked = buf[0] & RMI_F34_IS_UNLOCKED;;
272		m_hasConfigID = buf[0] & RMI_F34_HAS_CONFIG_ID;
273
274		++queryAddr;
275
276		rc = m_device.Read(queryAddr, buf, 2);
277		if (rc != 2)
278			return UPDATE_FAIL_READ_F34_QUERIES;
279
280		m_blockSize = extract_short(buf + RMI_F34_BLOCK_SIZE_V1_OFFSET);
281
282		++queryAddr;
283
284		rc = m_device.Read(queryAddr, buf, 8);
285		if (rc != 8)
286			return UPDATE_FAIL_READ_F34_QUERIES;
287
288		m_fwBlockCount = extract_short(buf + RMI_F34_FW_BLOCKS_V1_OFFSET);
289		m_configBlockCount = extract_short(buf + RMI_F34_CONFIG_BLOCKS_V1_OFFSET);
290	} else {
291		rc = m_device.Read(queryAddr, buf, RMI_F34_QUERY_SIZE);
292		if (rc != RMI_F34_QUERY_SIZE)
293			return UPDATE_FAIL_READ_F34_QUERIES;
294
295		m_hasNewRegmap = buf[0] & RMI_F34_HAS_NEW_REG_MAP;
296		m_unlocked = buf[0] & RMI_F34_IS_UNLOCKED;;
297		m_hasConfigID = buf[0] & RMI_F34_HAS_CONFIG_ID;
298		m_blockSize = extract_short(buf + RMI_F34_BLOCK_SIZE_OFFSET);
299		m_fwBlockCount = extract_short(buf + RMI_F34_FW_BLOCKS_OFFSET);
300		m_configBlockCount = extract_short(buf + RMI_F34_CONFIG_BLOCKS_OFFSET);
301	}
302
303	idStr[0] = m_bootloaderID[0];
304	idStr[1] = m_bootloaderID[1];
305	idStr[2] = 0;
306
307	fprintf(stdout, "F34 bootloader id: %s (%#04x %#04x)\n", idStr, m_bootloaderID[0],
308		m_bootloaderID[1]);
309	fprintf(stdout, "F34 has config id: %d\n", m_hasConfigID);
310	fprintf(stdout, "F34 unlocked:      %d\n", m_unlocked);
311	fprintf(stdout, "F34 new reg map:   %d\n", m_hasNewRegmap);
312	fprintf(stdout, "F34 block size:    %d\n", m_blockSize);
313	fprintf(stdout, "F34 fw blocks:     %d\n", m_fwBlockCount);
314	fprintf(stdout, "F34 config blocks: %d\n", m_configBlockCount);
315	fprintf(stdout, "\n");
316
317	if (f34Version == 0x1)
318		m_f34StatusAddr = m_f34.GetDataBase() + 2;
319	else
320		m_f34StatusAddr = m_f34.GetDataBase() + RMI_F34_BLOCK_DATA_OFFSET + m_blockSize;
321
322	return UPDATE_SUCCESS;
323}
324
325int RMI4Update::ReadF34Controls()
326{
327	int rc;
328	unsigned char buf[2];
329
330	if (m_f34.GetFunctionVersion() == 0x1) {
331		rc = m_device.Read(m_f34StatusAddr, buf, 2);
332		if (rc != 2)
333			return UPDATE_FAIL_READ_F34_CONTROLS;
334
335		m_f34Command = buf[0] & RMI_F34_COMMAND_V1_MASK;
336		m_f34Status = buf[1] & RMI_F34_STATUS_V1_MASK;
337		m_programEnabled = !!(buf[1] & RMI_F34_ENABLED_MASK);
338
339	} else {
340		rc = m_device.Read(m_f34StatusAddr, buf, 1);
341		if (rc != 1)
342			return UPDATE_FAIL_READ_F34_CONTROLS;
343
344		m_f34Command = buf[0] & RMI_F34_COMMAND_MASK;
345		m_f34Status = (buf[0] >> RMI_F34_STATUS_SHIFT) & RMI_F34_STATUS_MASK;
346		m_programEnabled = !!(buf[0] & RMI_F34_ENABLED_MASK);
347	}
348
349	return UPDATE_SUCCESS;
350}
351
352int RMI4Update::WriteBootloaderID()
353{
354	int rc;
355	int blockDataOffset = RMI_F34_BLOCK_DATA_OFFSET;
356
357	if (m_f34.GetFunctionVersion() == 0x1)
358		blockDataOffset = RMI_F34_BLOCK_DATA_V1_OFFSET;
359
360	rc = m_device.Write(m_f34.GetDataBase() + blockDataOffset,
361				m_bootloaderID, RMI_BOOTLOADER_ID_SIZE);
362	if (rc != RMI_BOOTLOADER_ID_SIZE)
363		return UPDATE_FAIL_WRITE_BOOTLOADER_ID;
364
365	return UPDATE_SUCCESS;
366}
367
368int RMI4Update::EnterFlashProgramming()
369{
370	int rc;
371	unsigned char f01Control_0;
372	const unsigned char enableProg = RMI_F34_ENABLE_FLASH_PROG;
373
374	rc = WriteBootloaderID();
375	if (rc != UPDATE_SUCCESS)
376		return rc;
377
378	fprintf(stdout, "Enabling flash programming.\n");
379	rc = m_device.Write(m_f34StatusAddr, &enableProg, 1);
380	if (rc != 1)
381		return UPDATE_FAIL_ENABLE_FLASH_PROGRAMMING;
382
383	Sleep(RMI_F34_ENABLE_WAIT_MS);
384	m_device.RebindDriver();
385	rc = WaitForIdle(0);
386	if (rc != UPDATE_SUCCESS)
387		return UPDATE_FAIL_NOT_IN_IDLE_STATE;
388
389	if (!m_programEnabled)
390		return UPDATE_FAIL_PROGRAMMING_NOT_ENABLED;
391
392	fprintf(stdout, "Programming is enabled.\n");
393	rc = FindUpdateFunctions();
394	if (rc != UPDATE_SUCCESS)
395		return rc;
396
397	rc = m_device.Read(m_f01.GetDataBase(), &m_deviceStatus, 1);
398	if (rc != 1)
399		return UPDATE_FAIL_READ_DEVICE_STATUS;
400
401	if (!RMI_F01_STATUS_BOOTLOADER(m_deviceStatus))
402		return UPDATE_FAIL_DEVICE_NOT_IN_BOOTLOADER;
403
404	rc = ReadF34Queries();
405	if (rc != UPDATE_SUCCESS)
406		return rc;
407
408	rc = m_device.Read(m_f01.GetControlBase(), &f01Control_0, 1);
409	if (rc != 1)
410		return UPDATE_FAIL_READ_F01_CONTROL_0;
411
412	f01Control_0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
413	f01Control_0 = (f01Control_0 & ~RMI_F01_CTRL0_SLEEP_MODE_MASK) | RMI_SLEEP_MODE_NORMAL;
414
415	rc = m_device.Write(m_f01.GetControlBase(), &f01Control_0, 1);
416	if (rc != 1)
417		return UPDATE_FAIL_WRITE_F01_CONTROL_0;
418
419	return UPDATE_SUCCESS;
420}
421
422int RMI4Update::WriteBlocks(unsigned char *block, unsigned short count, unsigned char cmd)
423{
424	int blockNum;
425	unsigned char zeros[] = { 0, 0 };
426	int rc;
427	unsigned short addr;
428	unsigned char *blockWithCmd = (unsigned char *)alloca(m_blockSize + 1);
429
430	if (m_f34.GetFunctionVersion() == 0x1)
431		addr = m_f34.GetDataBase() + RMI_F34_BLOCK_DATA_V1_OFFSET;
432	else
433		addr = m_f34.GetDataBase() + RMI_F34_BLOCK_DATA_OFFSET;
434
435	rc = m_device.Write(m_f34.GetDataBase(), zeros, 2);
436	if (rc != 2)
437		return UPDATE_FAIL_WRITE_INITIAL_ZEROS;
438
439	for (blockNum = 0; blockNum < count; ++blockNum) {
440		if (m_writeBlockWithCmd) {
441			memcpy(blockWithCmd, block, m_blockSize);
442			blockWithCmd[m_blockSize] = cmd;
443
444			rc = m_device.Write(addr, blockWithCmd, m_blockSize + 1);
445			if (rc != m_blockSize + 1) {
446				fprintf(stderr, "failed to write block %d\n", blockNum);
447				return UPDATE_FAIL_WRITE_BLOCK;
448			}
449		} else {
450			rc = m_device.Write(addr, block, m_blockSize);
451			if (rc != m_blockSize) {
452				fprintf(stderr, "failed to write block %d\n", blockNum);
453				return UPDATE_FAIL_WRITE_BLOCK;
454			}
455
456			rc = m_device.Write(m_f34StatusAddr, &cmd, 1);
457			if (rc != 1) {
458				fprintf(stderr, "failed to write command for block %d\n", blockNum);
459				return UPDATE_FAIL_WRITE_FLASH_COMMAND;
460			}
461		}
462
463		rc = WaitForIdle(RMI_F34_IDLE_WAIT_MS, !m_writeBlockWithCmd);
464		if (rc != UPDATE_SUCCESS) {
465			fprintf(stderr, "failed to go into idle after writing block %d\n", blockNum);
466			return UPDATE_FAIL_NOT_IN_IDLE_STATE;
467		}
468
469		block += m_blockSize;
470	}
471
472	return UPDATE_SUCCESS;
473}
474
475/*
476 * This is a limited implementation of WaitForIdle which assumes WaitForAttention is supported
477 * this will be true for HID, but other protocols will need to revert polling. Polling
478 * is not implemented yet.
479 */
480int RMI4Update::WaitForIdle(int timeout_ms, bool readF34OnSucess)
481{
482	int rc = 0;
483	struct timeval tv;
484
485	if (timeout_ms > 0) {
486		tv.tv_sec = timeout_ms / 1000;
487		tv.tv_usec = (timeout_ms % 1000) * 1000;
488
489		rc = m_device.WaitForAttention(&tv, m_f34.GetInterruptMask());
490		if (rc == -ETIMEDOUT)
491			/*
492			 * If for some reason we are not getting attention reports for HID devices
493			 * then we can still continue after the timeout and read F34 status
494			 * but if we have to wait for the timeout to ellapse everytime then this
495			 * will be slow. If this message shows up a lot then something is wrong
496			 * with receiving attention reports and that should be fixed.
497			 */
498			fprintf(stderr, "Timed out waiting for attn report\n");
499	}
500
501	if (rc <= 0 || readF34OnSucess) {
502		rc = ReadF34Controls();
503		if (rc != UPDATE_SUCCESS)
504			return rc;
505
506		if (!m_f34Status && !m_f34Command) {
507			if (!m_programEnabled) {
508				fprintf(stderr, "Bootloader is idle but program_enabled bit isn't set.\n");
509				return UPDATE_FAIL_PROGRAMMING_NOT_ENABLED;
510			} else {
511				return UPDATE_SUCCESS;
512			}
513		}
514
515		fprintf(stderr, "ERROR: Waiting for idle status.\n");
516		fprintf(stderr, "Command: %#04x\n", m_f34Command);
517		fprintf(stderr, "Status:  %#04x\n", m_f34Status);
518		fprintf(stderr, "Enabled: %d\n", m_programEnabled);
519		fprintf(stderr, "Idle:    %d\n", !m_f34Command && !m_f34Status);
520
521		return UPDATE_FAIL_NOT_IN_IDLE_STATE;
522	}
523
524	return UPDATE_SUCCESS;
525}
526