blob: f46a09a09e9f16211f8b24edb729669d040794ba [file] [log] [blame]
Andrew Duggan052556f2014-04-16 11:32:30 -07001/*
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
Andrew Duggan4e811252014-04-03 15:17:57 -070018#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
26#include "rmi4update.h"
27
28#define RMI_F34_QUERY_SIZE 7
29#define RMI_F34_HAS_NEW_REG_MAP (1 << 0)
30#define RMI_F34_IS_UNLOCKED (1 << 1)
31#define RMI_F34_HAS_CONFIG_ID (1 << 2)
32#define RMI_F34_BLOCK_SIZE_OFFSET 1
33#define RMI_F34_FW_BLOCKS_OFFSET 3
34#define RMI_F34_CONFIG_BLOCKS_OFFSET 5
35
36#define RMI_F34_BLOCK_DATA_OFFSET 2
37
38#define RMI_F34_COMMAND_MASK 0x0F
39#define RMI_F34_STATUS_MASK 0x07
40#define RMI_F34_STATUS_SHIFT 4
41#define RMI_F34_ENABLED_MASK 0x80
42
43#define RMI_F34_WRITE_FW_BLOCK 0x02
44#define RMI_F34_ERASE_ALL 0x03
45#define RMI_F34_WRITE_LOCKDOWN_BLOCK 0x04
46#define RMI_F34_WRITE_CONFIG_BLOCK 0x06
47#define RMI_F34_ENABLE_FLASH_PROG 0x0f
48
49#define RMI_F34_ENABLE_WAIT_MS 300
50#define RMI_F34_ERASE_WAIT_MS (5 * 1000)
51#define RMI_F34_IDLE_WAIT_MS 500
52
53/* Most recent device status event */
54#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f)
55/* Indicates that flash programming is enabled (bootloader mode). */
56#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40))
57/* The device has lost its configuration for some reason. */
58#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80))
59
60/*
61 * Sleep mode controls power management on the device and affects all
62 * functions of the device.
63 */
64#define RMI_F01_CTRL0_SLEEP_MODE_MASK 0x03
65
66#define RMI_SLEEP_MODE_NORMAL 0x00
67#define RMI_SLEEP_MODE_SENSOR_SLEEP 0x01
68#define RMI_SLEEP_MODE_RESERVED0 0x02
69#define RMI_SLEEP_MODE_RESERVED1 0x03
70
71/*
72 * This bit disables whatever sleep mode may be selected by the sleep_mode
73 * field and forces the device to run at full power without sleeping.
74 */
75#define RMI_F01_CRTL0_NOSLEEP_BIT (1 << 2)
76
77int RMI4Update::UpdateFirmware(bool force)
78{
79 struct timespec start;
80 struct timespec end;
Andrew Duggan65e55532014-04-04 16:59:54 -070081 long long int duration_us = 0;
Andrew Duggan4e811252014-04-03 15:17:57 -070082 int rc;
83 const unsigned char eraseAll = RMI_F34_ERASE_ALL;
84
85 rc = FindUpdateFunctions();
86 if (rc != UPDATE_SUCCESS)
87 return rc;
88
89 rc = m_device.QueryBasicProperties();
90 if (rc < 0)
91 return UPDATE_FAIL_QUERY_BASIC_PROPERTIES;
92
93 fprintf(stdout, "Device Properties:\n");
94 m_device.PrintProperties();
95
96 rc = ReadF34Queries();
97 if (rc != UPDATE_SUCCESS)
98 return rc;
99
100 rc = m_firmwareImage.VerifyImage(GetFirmwareSize(), GetConfigSize());
101 if (rc != UPDATE_SUCCESS)
102 return rc;
103
104 rc = EnterFlashProgramming();
105 if (rc != UPDATE_SUCCESS) {
106 fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
107 return rc;
108 }
109
110 if (!force && m_firmwareImage.HasIO()) {
111 if (m_firmwareImage.GetFirmwareID() <= m_device.GetFirmwareID()) {
112 m_device.Reset();
113 return UPDATE_FAIL_FIRMWARE_IMAGE_IS_OLDER;
114 }
115 }
116
117 if (m_unlocked) {
118 if (m_firmwareImage.GetLockdownData()) {
119 fprintf(stdout, "Writing lockdown...\n");
120 clock_gettime(CLOCK_MONOTONIC, &start);
121 rc = WriteBlocks(m_firmwareImage.GetLockdownData(),
122 m_firmwareImage.GetLockdownSize() / 0x10,
123 RMI_F34_WRITE_LOCKDOWN_BLOCK);
124 if (rc != UPDATE_SUCCESS) {
125 fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
126 return rc;
127 }
128 clock_gettime(CLOCK_MONOTONIC, &end);
Andrew Duggan65e55532014-04-04 16:59:54 -0700129 duration_us = diff_time(&start, &end);
130 fprintf(stdout, "Done writing lockdown, time: %lld us.\n", duration_us);
Andrew Duggan4e811252014-04-03 15:17:57 -0700131 }
132
133 rc = EnterFlashProgramming();
134 if (rc != UPDATE_SUCCESS) {
135 fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
136 return rc;
137 }
138
139 }
140
141 rc = WriteBootloaderID();
142 if (rc != UPDATE_SUCCESS) {
143 fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
144 return rc;
145 }
146
147 fprintf(stdout, "Erasing FW...\n");
148 clock_gettime(CLOCK_MONOTONIC, &start);
149 rc = m_device.Write(m_f34StatusAddr, &eraseAll, 1);
150 if (rc < 0) {
151 fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(UPDATE_FAIL_ERASE_ALL));
152 return UPDATE_FAIL_ERASE_ALL;
153 }
154
155 rc = WaitForIdle(RMI_F34_ERASE_WAIT_MS);
156 if (rc != UPDATE_SUCCESS) {
157 fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
158 return rc;
159 }
160 clock_gettime(CLOCK_MONOTONIC, &end);
Andrew Duggan65e55532014-04-04 16:59:54 -0700161 duration_us = diff_time(&start, &end);
162 fprintf(stdout, "Erase complete, time: %lld us.\n", duration_us);
Andrew Duggan4e811252014-04-03 15:17:57 -0700163
164 if (m_firmwareImage.GetFirmwareData()) {
165 fprintf(stdout, "Writing firmware...\n");
166 clock_gettime(CLOCK_MONOTONIC, &start);
167 rc = WriteBlocks(m_firmwareImage.GetFirmwareData(), m_fwBlockCount,
168 RMI_F34_WRITE_FW_BLOCK);
169 if (rc != UPDATE_SUCCESS) {
170 fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
171 return rc;
172 }
173 clock_gettime(CLOCK_MONOTONIC, &end);
Andrew Duggan65e55532014-04-04 16:59:54 -0700174 duration_us = diff_time(&start, &end);
175 fprintf(stdout, "Done writing FW, time: %lld us.\n", duration_us);
Andrew Duggan4e811252014-04-03 15:17:57 -0700176 }
177
178 if (m_firmwareImage.GetConfigData()) {
179 fprintf(stdout, "Writing configuration...\n");
180 clock_gettime(CLOCK_MONOTONIC, &start);
181 rc = WriteBlocks(m_firmwareImage.GetConfigData(), m_configBlockCount,
182 RMI_F34_WRITE_CONFIG_BLOCK);
183 if (rc != UPDATE_SUCCESS) {
184 fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc));
185 return rc;
186 }
187 clock_gettime(CLOCK_MONOTONIC, &end);
Andrew Duggan65e55532014-04-04 16:59:54 -0700188 duration_us = diff_time(&start, &end);
189 fprintf(stdout, "Done writing config, time: %lld us.\n", duration_us);
Andrew Duggan4e811252014-04-03 15:17:57 -0700190 }
191 m_device.Reset();
192
193 return UPDATE_SUCCESS;
194
195}
196
197int RMI4Update::FindUpdateFunctions()
198{
199 if (0 > m_device.ScanPDT())
200 return UPDATE_FAIL_SCAN_PDT;
201
202 if (!m_device.GetFunction(m_f01, 0x01))
203 return UPDATE_FAIL_NO_FUNCTION_01;
204
205 if (!m_device.GetFunction(m_f34, 0x34))
206 return UPDATE_FAIL_NO_FUNCTION_34;
207
208 return UPDATE_SUCCESS;
209}
210
211int RMI4Update::ReadF34Queries()
212{
213 int rc;
214 unsigned char idStr[3];
215 unsigned char buf[RMI_F34_QUERY_SIZE];
216 unsigned short queryAddr = m_f34.GetQueryBase();
217
218 rc = m_device.Read(queryAddr, m_bootloaderID, RMI_BOOTLOADER_ID_SIZE);
219 if (rc < 0)
220 return UPDATE_FAIL_READ_BOOTLOADER_ID;
221
222 queryAddr += RMI_BOOTLOADER_ID_SIZE;
223
224 rc = m_device.Read(queryAddr, buf, RMI_F34_QUERY_SIZE);
225 if (rc < 0)
226 return UPDATE_FAIL_READ_F34_QUERIES;
227
228 m_hasNewRegmap = buf[0] & RMI_F34_HAS_NEW_REG_MAP;
229 m_unlocked = buf[0] & RMI_F34_IS_UNLOCKED;;
230 m_hasConfigID = buf[0] & RMI_F34_HAS_CONFIG_ID;
231 m_blockSize = extract_short(buf + RMI_F34_BLOCK_SIZE_OFFSET);
232 m_fwBlockCount = extract_short(buf + RMI_F34_FW_BLOCKS_OFFSET);
233 m_configBlockCount = extract_short(buf + RMI_F34_CONFIG_BLOCKS_OFFSET);
234
235 idStr[0] = m_bootloaderID[0];
236 idStr[1] = m_bootloaderID[1];
237 idStr[2] = 0;
238
239 fprintf(stdout, "F34 bootloader id: %s (%#04x %#04x)\n", idStr, m_bootloaderID[0],
240 m_bootloaderID[1]);
241 fprintf(stdout, "F34 has config id: %d\n", m_hasConfigID);
242 fprintf(stdout, "F34 unlocked: %d\n", m_unlocked);
243 fprintf(stdout, "F34 new reg map: %d\n", m_hasNewRegmap);
244 fprintf(stdout, "F34 block size: %d\n", m_blockSize);
245 fprintf(stdout, "F34 fw blocks: %d\n", m_fwBlockCount);
246 fprintf(stdout, "F34 config blocks: %d\n", m_configBlockCount);
247 fprintf(stdout, "\n");
248
249 m_f34StatusAddr = m_f34.GetDataBase() + RMI_F34_BLOCK_DATA_OFFSET + m_blockSize;
250
251 return UPDATE_SUCCESS;
252}
253
254int RMI4Update::ReadF34Controls()
255{
256 int rc;
257 unsigned char buf;
258
259 rc = m_device.Read(m_f34StatusAddr, &buf, 1);
260 if (rc < 0)
261 return UPDATE_FAIL_READ_F34_CONTROLS;
262
263 m_f34Command = buf & RMI_F34_COMMAND_MASK;
264 m_f34Status = (buf >> RMI_F34_STATUS_SHIFT) & RMI_F34_STATUS_MASK;
265 m_programEnabled = !!(buf & RMI_F34_ENABLED_MASK);
266
267 return UPDATE_SUCCESS;
268}
269
270int RMI4Update::WriteBootloaderID()
271{
272 int rc;
273
274 rc = m_device.Write(m_f34.GetDataBase() + RMI_F34_BLOCK_DATA_OFFSET,
275 m_bootloaderID, RMI_BOOTLOADER_ID_SIZE);
276 if (rc < 0)
277 return UPDATE_FAIL_WRITE_BOOTLOADER_ID;
278
279 return UPDATE_SUCCESS;
280}
281
282int RMI4Update::EnterFlashProgramming()
283{
284 int rc;
285 unsigned char f01Control_0;
286 const unsigned char enableProg = RMI_F34_ENABLE_FLASH_PROG;
287
288 rc = WriteBootloaderID();
289 if (rc != UPDATE_SUCCESS)
290 return rc;
291
292 fprintf(stdout, "Enabling flash programming.\n");
293 rc = m_device.Write(m_f34StatusAddr, &enableProg, 1);
294 if (rc < 0)
295 return UPDATE_FAIL_ENABLE_FLASH_PROGRAMMING;
296
297 rc = WaitForIdle(RMI_F34_ENABLE_WAIT_MS);
298 if (rc != UPDATE_SUCCESS)
299 return UPDATE_FAIL_NOT_IN_IDLE_STATE;
300
301 if (!m_programEnabled)
302 return UPDATE_FAIL_PROGRAMMING_NOT_ENABLED;
303
304 fprintf(stdout, "HOORAY! Programming is enabled!\n");
305 rc = FindUpdateFunctions();
306 if (rc != UPDATE_SUCCESS)
307 return rc;
308
309 rc = m_device.Read(m_f01.GetDataBase(), &m_deviceStatus, 1);
310 if (rc < 0)
311 return UPDATE_FAIL_READ_DEVICE_STATUS;
312
313 if (!RMI_F01_STATUS_BOOTLOADER(m_deviceStatus))
314 return UPDATE_FAIL_DEVICE_NOT_IN_BOOTLOADER;
315
316 rc = ReadF34Queries();
317 if (rc != UPDATE_SUCCESS)
318 return rc;
319
320 rc = m_device.Read(m_f01.GetControlBase(), &f01Control_0, 1);
321 if (rc < 0)
322 return UPDATE_FAIL_READ_F01_CONTROL_0;
323
324 f01Control_0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
325 f01Control_0 = (f01Control_0 & ~RMI_F01_CTRL0_SLEEP_MODE_MASK) | RMI_SLEEP_MODE_NORMAL;
326
327 rc = m_device.Write(m_f01.GetControlBase(), &f01Control_0, 1);
328 if (rc < 0)
329 return UPDATE_FAIL_WRITE_F01_CONTROL_0;
330
331 return UPDATE_SUCCESS;
332}
333
334int RMI4Update::WriteBlocks(unsigned char *block, unsigned short count, unsigned char cmd)
335{
336 int blockNum;
337 unsigned char zeros[] = { 0, 0 };
338 int rc;
339 unsigned short addr = m_f34.GetDataBase() + RMI_F34_BLOCK_DATA_OFFSET;
340
341 rc = m_device.Write(m_f34.GetDataBase(), zeros, 2);
342 if (rc < 0)
343 return UPDATE_FAIL_WRITE_INITIAL_ZEROS;
344
345 for (blockNum = 0; blockNum < count; ++blockNum) {
346 rc = m_device.Write(addr, block, m_blockSize);
347 if (rc < 0) {
348 fprintf(stderr, "failed to write block %d\n", blockNum);
349 return UPDATE_FAIL_WRITE_BLOCK;
350 }
351
352
353 rc = m_device.Write(m_f34StatusAddr, &cmd, 1);
354 if (rc < 0) {
355 fprintf(stderr, "failed to write command for block %d\n", blockNum);
356 return UPDATE_FAIL_WRITE_FLASH_COMMAND;
357 }
358
359 rc = WaitForIdle(RMI_F34_IDLE_WAIT_MS);
360 if (rc != UPDATE_SUCCESS) {
361 fprintf(stderr, "failed to go into idle after writing block %d\n", blockNum);
362 return UPDATE_FAIL_NOT_IN_IDLE_STATE;
363 }
364
365 block += m_blockSize;
366 }
367
368 return UPDATE_SUCCESS;
369}
370
371/*
372 * This is a limited implementation of WaitForIdle which assumes WaitForAttention is supported
373 * this will be true for HID, but other protocols will need to revert polling. Polling
374 * is not implemented yet.
375 */
376int RMI4Update::WaitForIdle(int timeout_ms)
377{
378 int rc;
379 struct timeval tv;
380
381 tv.tv_sec = timeout_ms / 1000;
382 tv.tv_usec = (timeout_ms % 1000) * 1000;
383
384 rc = m_device.WaitForAttention(&tv);
385 if (rc > 0) {
386 rc = ReadF34Controls();
387 if (rc != UPDATE_SUCCESS)
388 return rc;
389
390 if (!m_f34Status && !m_f34Command) {
391 if (!m_programEnabled) {
392 fprintf(stderr, "Bootloader is idle but program_enabled bit isn't set.\n");
393 return UPDATE_FAIL_PROGRAMMING_NOT_ENABLED;
394 } else {
395 return UPDATE_SUCCESS;
396 }
397 }
398
399 fprintf(stderr, "ERROR: Waiting for idle status.\n");
400 fprintf(stderr, "Command: %#04x\n", m_f34Command);
401 fprintf(stderr, "Status: %#04x\n", m_f34Status);
402 fprintf(stderr, "Enabled: %d\n", m_programEnabled);
403 fprintf(stderr, "Idle: %d\n", !m_f34Command && !m_f34Status);
404
405 return UPDATE_FAIL_NOT_IN_IDLE_STATE;
406 }
407
408 return UPDATE_FAIL_TIMEOUT;
409}