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