rmi4update.cpp revision a0b675a6bb76681a4c1a341a0772cdbe5a04b286
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	fprintf(stdout, "Device Properties:\n");
104	m_device.PrintProperties();
105
106	rc = ReadF34Queries();
107	if (rc != UPDATE_SUCCESS)
108		return rc;
109
110	rc = m_firmwareImage.VerifyImageMatchesDevice(GetFirmwareSize(), GetConfigSize());
111	if (rc != UPDATE_SUCCESS)
112		return rc;
113
114	rc = EnterFlashProgramming();
115	if (rc != UPDATE_SUCCESS) {
116		fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
117		return rc;
118	}
119
120	if (!force && m_firmwareImage.HasIO()) {
121		if (m_firmwareImage.GetFirmwareID() <= m_device.GetFirmwareID()) {
122			m_device.Reset();
123			return UPDATE_FAIL_FIRMWARE_IMAGE_IS_OLDER;
124		}
125	}
126
127	if (performLockdown && m_unlocked) {
128		if (m_firmwareImage.GetLockdownData()) {
129			fprintf(stdout, "Writing lockdown...\n");
130			clock_gettime(CLOCK_MONOTONIC, &start);
131			rc = WriteBlocks(m_firmwareImage.GetLockdownData(),
132					m_firmwareImage.GetLockdownSize() / 0x10,
133					RMI_F34_WRITE_LOCKDOWN_BLOCK);
134			if (rc != UPDATE_SUCCESS) {
135				fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
136				return rc;
137			}
138			clock_gettime(CLOCK_MONOTONIC, &end);
139			duration_us = diff_time(&start, &end);
140			fprintf(stdout, "Done writing lockdown, time: %lld us.\n", duration_us);
141		}
142
143		rc = EnterFlashProgramming();
144		if (rc != UPDATE_SUCCESS) {
145			fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
146			return rc;
147		}
148
149	}
150
151	rc = WriteBootloaderID();
152	if (rc != UPDATE_SUCCESS) {
153		fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
154		return rc;
155	}
156
157	fprintf(stdout, "Erasing FW...\n");
158	clock_gettime(CLOCK_MONOTONIC, &start);
159	rc = m_device.Write(m_f34StatusAddr, &eraseAll, 1);
160	if (rc < 0) {
161		fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(UPDATE_FAIL_ERASE_ALL));
162		return UPDATE_FAIL_ERASE_ALL;
163	}
164
165	rc = WaitForIdle(RMI_F34_ERASE_WAIT_MS);
166	if (rc != UPDATE_SUCCESS) {
167		fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
168		return rc;
169	}
170	clock_gettime(CLOCK_MONOTONIC, &end);
171	duration_us = diff_time(&start, &end);
172	fprintf(stdout, "Erase complete, time: %lld us.\n", duration_us);
173
174	if (m_firmwareImage.GetFirmwareData()) {
175		fprintf(stdout, "Writing firmware...\n");
176		clock_gettime(CLOCK_MONOTONIC, &start);
177		rc = WriteBlocks(m_firmwareImage.GetFirmwareData(), m_fwBlockCount,
178						RMI_F34_WRITE_FW_BLOCK);
179		if (rc != UPDATE_SUCCESS) {
180			fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
181			return rc;
182		}
183		clock_gettime(CLOCK_MONOTONIC, &end);
184		duration_us = diff_time(&start, &end);
185		fprintf(stdout, "Done writing FW, time: %lld us.\n", duration_us);
186	}
187
188	if (m_firmwareImage.GetConfigData()) {
189		fprintf(stdout, "Writing configuration...\n");
190		clock_gettime(CLOCK_MONOTONIC, &start);
191		rc = WriteBlocks(m_firmwareImage.GetConfigData(), m_configBlockCount,
192				RMI_F34_WRITE_CONFIG_BLOCK);
193		if (rc != UPDATE_SUCCESS) {
194			fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
195			return rc;
196		}
197		clock_gettime(CLOCK_MONOTONIC, &end);
198		duration_us = diff_time(&start, &end);
199		fprintf(stdout, "Done writing config, time: %lld us.\n", duration_us);
200	}
201	m_device.Reset();
202
203	return UPDATE_SUCCESS;
204
205}
206
207int RMI4Update::FindUpdateFunctions()
208{
209	if (0 > m_device.ScanPDT())
210		return UPDATE_FAIL_SCAN_PDT;
211
212	if (!m_device.GetFunction(m_f01, 0x01))
213		return UPDATE_FAIL_NO_FUNCTION_01;
214
215	if (!m_device.GetFunction(m_f34, 0x34))
216		return UPDATE_FAIL_NO_FUNCTION_34;
217
218	return UPDATE_SUCCESS;
219}
220
221int RMI4Update::ReadF34Queries()
222{
223	int rc;
224	unsigned char idStr[3];
225	unsigned char buf[8];
226	unsigned short queryAddr = m_f34.GetQueryBase();
227	unsigned short f34Version = m_f34.GetFunctionVersion();
228	unsigned short querySize;
229
230	if (f34Version == 0x1)
231		querySize = 8;
232	else
233		querySize = 2;
234
235	rc = m_device.Read(queryAddr, m_bootloaderID, RMI_BOOTLOADER_ID_SIZE);
236	if (rc < 0)
237		return UPDATE_FAIL_READ_BOOTLOADER_ID;
238
239	if (f34Version == 0x1)
240		++queryAddr;
241	else
242		queryAddr += querySize;
243
244	if (f34Version == 0x1) {
245		rc = m_device.Read(queryAddr, buf, 1);
246		if (rc < 0)
247			return UPDATE_FAIL_READ_F34_QUERIES;
248
249		m_hasNewRegmap = buf[0] & RMI_F34_HAS_NEW_REG_MAP;
250		m_unlocked = buf[0] & RMI_F34_IS_UNLOCKED;;
251		m_hasConfigID = buf[0] & RMI_F34_HAS_CONFIG_ID;
252
253		++queryAddr;
254
255		rc = m_device.Read(queryAddr, buf, 2);
256		if (rc < 0)
257			return UPDATE_FAIL_READ_F34_QUERIES;
258
259		m_blockSize = extract_short(buf + RMI_F34_BLOCK_SIZE_V1_OFFSET);
260
261		++queryAddr;
262
263		rc = m_device.Read(queryAddr, buf, 8);
264		if (rc < 0)
265			return UPDATE_FAIL_READ_F34_QUERIES;
266
267		m_fwBlockCount = extract_short(buf + RMI_F34_FW_BLOCKS_V1_OFFSET);
268		m_configBlockCount = extract_short(buf + RMI_F34_CONFIG_BLOCKS_V1_OFFSET);
269	} else {
270		rc = m_device.Read(queryAddr, buf, RMI_F34_QUERY_SIZE);
271		if (rc < 0)
272			return UPDATE_FAIL_READ_F34_QUERIES;
273
274		m_hasNewRegmap = buf[0] & RMI_F34_HAS_NEW_REG_MAP;
275		m_unlocked = buf[0] & RMI_F34_IS_UNLOCKED;;
276		m_hasConfigID = buf[0] & RMI_F34_HAS_CONFIG_ID;
277		m_blockSize = extract_short(buf + RMI_F34_BLOCK_SIZE_OFFSET);
278		m_fwBlockCount = extract_short(buf + RMI_F34_FW_BLOCKS_OFFSET);
279		m_configBlockCount = extract_short(buf + RMI_F34_CONFIG_BLOCKS_OFFSET);
280	}
281
282	idStr[0] = m_bootloaderID[0];
283	idStr[1] = m_bootloaderID[1];
284	idStr[2] = 0;
285
286	fprintf(stdout, "F34 bootloader id: %s (%#04x %#04x)\n", idStr, m_bootloaderID[0],
287		m_bootloaderID[1]);
288	fprintf(stdout, "F34 has config id: %d\n", m_hasConfigID);
289	fprintf(stdout, "F34 unlocked:      %d\n", m_unlocked);
290	fprintf(stdout, "F34 new reg map:   %d\n", m_hasNewRegmap);
291	fprintf(stdout, "F34 block size:    %d\n", m_blockSize);
292	fprintf(stdout, "F34 fw blocks:     %d\n", m_fwBlockCount);
293	fprintf(stdout, "F34 config blocks: %d\n", m_configBlockCount);
294	fprintf(stdout, "\n");
295
296	if (f34Version == 0x1)
297		m_f34StatusAddr = m_f34.GetDataBase() + 2;
298	else
299		m_f34StatusAddr = m_f34.GetDataBase() + RMI_F34_BLOCK_DATA_OFFSET + m_blockSize;
300
301	return UPDATE_SUCCESS;
302}
303
304int RMI4Update::ReadF34Controls()
305{
306	int rc;
307	unsigned char buf[2];
308
309	if (m_f34.GetFunctionVersion() == 0x1) {
310		rc = m_device.Read(m_f34StatusAddr, buf, 2);
311		if (rc < 0)
312			return UPDATE_FAIL_READ_F34_CONTROLS;
313
314		m_f34Command = buf[0] & RMI_F34_COMMAND_V1_MASK;
315		m_f34Status = buf[1] & RMI_F34_STATUS_V1_MASK;
316		m_programEnabled = !!(buf[1] & RMI_F34_ENABLED_MASK);
317
318	} else {
319		rc = m_device.Read(m_f34StatusAddr, buf, 1);
320		if (rc < 0)
321			return UPDATE_FAIL_READ_F34_CONTROLS;
322
323		m_f34Command = buf[0] & RMI_F34_COMMAND_MASK;
324		m_f34Status = (buf[0] >> RMI_F34_STATUS_SHIFT) & RMI_F34_STATUS_MASK;
325		m_programEnabled = !!(buf[0] & RMI_F34_ENABLED_MASK);
326	}
327
328	return UPDATE_SUCCESS;
329}
330
331int RMI4Update::WriteBootloaderID()
332{
333	int rc;
334	int blockDataOffset = RMI_F34_BLOCK_DATA_OFFSET;
335
336	if (m_f34.GetFunctionVersion() == 0x1)
337		blockDataOffset = RMI_F34_BLOCK_DATA_V1_OFFSET;
338
339	rc = m_device.Write(m_f34.GetDataBase() + blockDataOffset,
340				m_bootloaderID, RMI_BOOTLOADER_ID_SIZE);
341	if (rc < 0)
342		return UPDATE_FAIL_WRITE_BOOTLOADER_ID;
343
344	return UPDATE_SUCCESS;
345}
346
347int RMI4Update::EnterFlashProgramming()
348{
349	int rc;
350	unsigned char f01Control_0;
351	const unsigned char enableProg = RMI_F34_ENABLE_FLASH_PROG;
352
353	rc = WriteBootloaderID();
354	if (rc != UPDATE_SUCCESS)
355		return rc;
356
357	fprintf(stdout, "Enabling flash programming.\n");
358	rc = m_device.Write(m_f34StatusAddr, &enableProg, 1);
359	if (rc < 0)
360		return UPDATE_FAIL_ENABLE_FLASH_PROGRAMMING;
361
362	rc = WaitForIdle(RMI_F34_ENABLE_WAIT_MS);
363	if (rc != UPDATE_SUCCESS)
364		return UPDATE_FAIL_NOT_IN_IDLE_STATE;
365
366	if (!m_programEnabled)
367		return UPDATE_FAIL_PROGRAMMING_NOT_ENABLED;
368
369	fprintf(stdout, "Programming is enabled.\n");
370	rc = FindUpdateFunctions();
371	if (rc != UPDATE_SUCCESS)
372		return rc;
373
374	rc = m_device.Read(m_f01.GetDataBase(), &m_deviceStatus, 1);
375	if (rc < 0)
376		return UPDATE_FAIL_READ_DEVICE_STATUS;
377
378	if (!RMI_F01_STATUS_BOOTLOADER(m_deviceStatus))
379		return UPDATE_FAIL_DEVICE_NOT_IN_BOOTLOADER;
380
381	rc = ReadF34Queries();
382	if (rc != UPDATE_SUCCESS)
383		return rc;
384
385	rc = m_device.Read(m_f01.GetControlBase(), &f01Control_0, 1);
386	if (rc < 0)
387		return UPDATE_FAIL_READ_F01_CONTROL_0;
388
389	f01Control_0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
390	f01Control_0 = (f01Control_0 & ~RMI_F01_CTRL0_SLEEP_MODE_MASK) | RMI_SLEEP_MODE_NORMAL;
391
392	rc = m_device.Write(m_f01.GetControlBase(), &f01Control_0, 1);
393	if (rc < 0)
394		return UPDATE_FAIL_WRITE_F01_CONTROL_0;
395
396	return UPDATE_SUCCESS;
397}
398
399int RMI4Update::WriteBlocks(unsigned char *block, unsigned short count, unsigned char cmd)
400{
401	int blockNum;
402	unsigned char zeros[] = { 0, 0 };
403	int rc;
404	unsigned short addr;
405
406	if (m_f34.GetFunctionVersion() == 0x1)
407		addr = m_f34.GetDataBase() + RMI_F34_BLOCK_DATA_V1_OFFSET;
408	else
409		addr = m_f34.GetDataBase() + RMI_F34_BLOCK_DATA_OFFSET;
410
411	rc = m_device.Write(m_f34.GetDataBase(), zeros, 2);
412	if (rc < 0)
413		return UPDATE_FAIL_WRITE_INITIAL_ZEROS;
414
415	for (blockNum = 0; blockNum < count; ++blockNum) {
416		rc = m_device.Write(addr, block, m_blockSize);
417		if (rc < 0) {
418			fprintf(stderr, "failed to write block %d\n", blockNum);
419			return UPDATE_FAIL_WRITE_BLOCK;
420		}
421
422		rc = m_device.Write(m_f34StatusAddr, &cmd, 1);
423		if (rc < 0) {
424			fprintf(stderr, "failed to write command for block %d\n", blockNum);
425			return UPDATE_FAIL_WRITE_FLASH_COMMAND;
426		}
427
428		rc = WaitForIdle(RMI_F34_IDLE_WAIT_MS);
429		if (rc != UPDATE_SUCCESS) {
430			fprintf(stderr, "failed to go into idle after writing block %d\n", blockNum);
431			return UPDATE_FAIL_NOT_IN_IDLE_STATE;
432		}
433
434		block += m_blockSize;
435	}
436
437	return UPDATE_SUCCESS;
438}
439
440/*
441 * This is a limited implementation of WaitForIdle which assumes WaitForAttention is supported
442 * this will be true for HID, but other protocols will need to revert polling. Polling
443 * is not implemented yet.
444 */
445int RMI4Update::WaitForIdle(int timeout_ms)
446{
447	int rc;
448	struct timeval tv;
449
450	tv.tv_sec = timeout_ms / 1000;
451	tv.tv_usec = (timeout_ms % 1000) * 1000;
452
453	rc = m_device.WaitForAttention(&tv);
454	if (rc == -ETIMEDOUT)
455		/*
456		 * If for some reason we are not getting attention reports for HID devices
457		 * then we can still continue after the timeout and read F34 status
458		 * but if we have to wait for the timeout to ellapse everytime then this
459		 * will be slow. If this message shows up a lot then something is wrong
460		 * with receiving attention reports and that should be fixed.
461		 */
462		fprintf(stderr, "Timed out waiting for attn report\n");
463
464	rc = ReadF34Controls();
465	if (rc != UPDATE_SUCCESS)
466		return rc;
467
468	if (!m_f34Status && !m_f34Command) {
469		if (!m_programEnabled) {
470			fprintf(stderr, "Bootloader is idle but program_enabled bit isn't set.\n");
471			return UPDATE_FAIL_PROGRAMMING_NOT_ENABLED;
472		} else {
473			return UPDATE_SUCCESS;
474		}
475	}
476
477	fprintf(stderr, "ERROR: Waiting for idle status.\n");
478	fprintf(stderr, "Command: %#04x\n", m_f34Command);
479	fprintf(stderr, "Status:  %#04x\n", m_f34Status);
480	fprintf(stderr, "Enabled: %d\n", m_programEnabled);
481	fprintf(stderr, "Idle:    %d\n", !m_f34Command && !m_f34Status);
482
483	return UPDATE_FAIL_NOT_IN_IDLE_STATE;
484}
485