• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
UpdateFirmware(bool force,bool performLockdown)87 int 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 
209 reset:
210 	m_device.Reset();
211 	m_device.RebindDriver();
212 	return rc;
213 
214 }
215 
DisableNonessentialInterupts()216 int 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 
FindUpdateFunctions()228 int 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 
ReadF34Queries()242 int 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 
ReadF34Controls()325 int 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 
WriteBootloaderID()352 int 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 
EnterFlashProgramming()368 int 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 
WriteBlocks(unsigned char * block,unsigned short count,unsigned char cmd)422 int 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  */
WaitForIdle(int timeout_ms,bool readF34OnSucess)480 int 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