1 /*
2 * vim:tw=80:ai:tabstop=4:softtabstop=4:shiftwidth=4:expandtab
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * (C) Copyright Kevin Timmerman 2007
19 * (C) Copyright Phil Dibowitz 2007
20 */
21
22 #include "remote.h"
23
24 #include <string.h>
25 #include <errno.h>
26
27 #include "libconcord.h"
28 #include "lc_internal.h"
29 #include "hid.h"
30 #include "protocol.h"
31 #include "remote_info.h"
32
33 #define GUID_STR \
34 "{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}"
35
setup_ri_pointers(TRemoteInfo & ri)36 void setup_ri_pointers(TRemoteInfo &ri)
37 {
38 unsigned int u;
39 for (u = 0; u < sizeof(FlashList)/sizeof(TFlash)-1; ++u) {
40 if (ri.flash_id == FlashList[u].id
41 && ri.flash_mfg == FlashList[u].mfg)
42 break;
43 }
44 ri.flash = &FlashList[u];
45
46 ri.arch = (ri.architecture < sizeof(ArchList)/sizeof(TArchInfo))
47 ? &ArchList[ri.architecture] : NULL;
48
49 ri.model = (ri.skin < max_model)
50 ? &ModelList[ri.skin] : &ModelList[max_model];
51 }
52
make_guid(const uint8_t * const in,char * & out)53 void make_guid(const uint8_t * const in, char*&out)
54 {
55 char x[48];
56 // Non-HID remotes (ZWave-HID, ZWave-USBNet, MH) as well as Arch 14 seem
57 // to use a more normal byte ordering for serial #'s.
58 if (is_z_remote() || is_mh_remote() || (get_arch() == 14)) {
59 sprintf(x, GUID_STR, in[0], in[1], in[2], in[3], in[4], in[5], in[6],
60 in[7], in[8], in[9], in[10], in[11], in[12], in[13], in[14],
61 in[15]);
62 }
63 else {
64 sprintf(x, GUID_STR, in[3], in[2], in[1], in[0], in[5], in[4], in[7],
65 in[6], in[8], in[9], in[10], in[11], in[12], in[13], in[14],
66 in[15]);
67 }
68 out = strdup(x);
69 }
70
make_serial(uint8_t * ser,TRemoteInfo & ri)71 void make_serial(uint8_t *ser, TRemoteInfo &ri)
72 {
73 make_guid(ser, ri.serial1);
74 make_guid(ser+16, ri.serial2);
75 make_guid(ser+32, ri.serial3);
76 }
77
Reset(uint8_t kind)78 int CRemote::Reset(uint8_t kind)
79 {
80 uint8_t reset_cmd[64] = { COMMAND_RESET, kind };
81 int err;
82
83 err = HID_WriteReport(reset_cmd);
84 /*
85 * Certain remotes (e.g., the 785) do not get a successful return from
86 * HID_WriteReport even though the reset succeeds. Ignore this.
87 */
88 if (err == -ENODEV) {
89 debug("Ignoring error from reset command");
90 err = 0;
91 }
92 return err;
93 }
94
95 /*
96 * Send the GET_VERSION command to the remote, and read the response.
97 *
98 * Then populate our struct with all the relevant info.
99 */
GetIdentity(TRemoteInfo & ri,THIDINFO & hid,lc_callback cb,void * cb_arg,uint32_t cb_stage)100 int CRemote::GetIdentity(TRemoteInfo &ri, THIDINFO &hid, lc_callback cb,
101 void *cb_arg, uint32_t cb_stage)
102 {
103 int err = 0;
104 uint32_t cb_count = 0;
105
106 const uint8_t qid[64] = { COMMAND_GET_VERSION };
107
108 if ((err = HID_WriteReport(qid))) {
109 debug("Failed to write to remote");
110 return 1;
111 }
112
113 uint8_t rsp[68];
114 if ((err = HID_ReadReport(rsp))) {
115 debug("Failed to read from remote");
116 return 1;
117 }
118
119 /*
120 * See specs/protocol.txt for format
121 */
122 const unsigned int rx_len = rsp[0] & 0x0F;
123
124 if ((rsp[0] & 0xF0) != RESPONSE_VERSION_DATA ||
125 (rx_len != 5 && rx_len != 7 && rx_len != 8)) {
126 debug("Bogus ident response: %02X", rsp[1]);
127 return LC_ERROR_INVALID_DATA_FROM_REMOTE;
128 }
129
130 ri.fw_ver_major = rsp[1] >> 4;
131 ri.fw_ver_minor = rsp[1] & 0x0F;
132 ri.hw_ver_major = rsp[2] >> 4;
133 ri.hw_ver_minor = rsp[2] & 0x0F;
134 ri.hw_ver_micro = 0; /* usbnet remotes have a non-zero micro version */
135 ri.flash_id = rsp[3];
136 ri.flash_mfg = rsp[4];
137 ri.architecture = rx_len < 6 ? 2 : rsp[5] >> 4;
138 ri.fw_type = rx_len < 6 ? 0 : rsp[5] & 0x0F;
139 ri.skin = rx_len < 6 ? 2 : rsp[6];
140 if (rx_len < 7) {
141 ri.protocol = 0;
142 } else if (rx_len < 8) {
143 ri.protocol = rsp[7];
144 } else {
145 ri.protocol = ri.architecture;
146 }
147
148 setup_ri_pointers(ri);
149
150 uint8_t rd[1024];
151 if ((err=ReadFlash(ri.arch->config_base, 1024, rd, ri.protocol, false))) {
152 debug("Error reading first k of config data");
153 return LC_ERROR_READ;
154 }
155 if (cb) {
156 cb(cb_stage, cb_count++, 1, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
157 }
158
159 /*
160 * Calculate cookie
161 * see specs/protocol.txt for more info
162 */
163 const uint32_t cookie = (ri.arch->cookie_size == 2)
164 ? rd[0] | (rd[1] << 8)
165 : rd[0] | (rd[1] << 8) | (rd[2] << 16) | (rd[3] << 24);
166
167 ri.valid_config = (cookie == ri.arch->cookie);
168
169 if (ri.valid_config) {
170 ri.max_config_size = (ri.flash->size << 10)
171 - (ri.arch->config_base - ri.arch->flash_base);
172 const uint32_t end = rd[ri.arch->end_vector]
173 | (rd[ri.arch->end_vector + 1] << 8)
174 | (rd[ri.arch->end_vector + 2] << 16);
175 ri.config_bytes_used =
176 (end - (ri.arch->config_base - ri.arch->flash_base))
177 + 4;
178 } else {
179 ri.config_bytes_used = 0;
180 ri.max_config_size = 1;
181 }
182
183 // read serial (see specs/protocol.txt for details)
184 switch (ri.arch->serial_location) {
185 case SERIAL_LOCATION_EEPROM:
186 err = ReadMiscByte(ri.arch->serial_address, SERIAL_SIZE,
187 COMMAND_MISC_EEPROM, rsp);
188 break;
189 case SERIAL_LOCATION_FLASH:
190 err = ReadFlash(ri.arch->serial_address, SERIAL_SIZE, rsp, ri.protocol);
191 break;
192 default:
193 debug("Invalid TArchInfo\n");
194 return LC_ERROR_READ;
195 }
196 if (err) {
197 debug("Couldn't read serial\n");
198 return LC_ERROR_READ;
199 }
200
201 if (cb) {
202 cb(cb_stage, cb_count++, 2, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
203 }
204
205 make_serial(rsp, ri);
206
207 return 0;
208 }
209
ReadFlash(uint32_t addr,const uint32_t len,uint8_t * rd,unsigned int protocol,bool verify,lc_callback cb,void * cb_arg,uint32_t cb_stage)210 int CRemote::ReadFlash(uint32_t addr, const uint32_t len, uint8_t *rd,
211 unsigned int protocol, bool verify, lc_callback cb,
212 void *cb_arg, uint32_t cb_stage)
213 {
214 uint32_t cb_count = 0;
215 const unsigned int max_chunk_len = protocol == 0 ? 700 : 1022;
216
217 /*
218 * This is a mapping of the lower-half of the first command byte to
219 * the size of the total command to be sent.
220 *
221 * See specs/protocol.txt for more * info.
222 */
223 // Protocol 0 (745, safe mode)
224 static const unsigned int dl0[16] =
225 { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
226 // All other protocols
227 static const unsigned int dlx[16] =
228 { 0, 0, 1, 2, 3, 4, 5, 6, 14, 30, 62, 0, 0, 0, 0, 0 };
229 const unsigned int *rxlenmap = protocol ? dlx : dl0;
230
231 uint8_t *pr = rd;
232 const uint32_t end = addr+len;
233 unsigned int bytes_read = 0;
234 int err = 0;
235
236 do {
237 static uint8_t cmd[64] = {0};
238 cmd[0] = COMMAND_READ_FLASH | 0x05;
239 cmd[1] = (addr >> 16) & 0xFF;
240 cmd[2] = (addr >> 8) & 0xFF;
241 cmd[3] = addr & 0xFF;
242 unsigned int chunk_len = end-addr;
243 if (chunk_len > max_chunk_len)
244 chunk_len = max_chunk_len;
245 cmd[4] = (chunk_len >> 8) & 0xFF;
246 cmd[5] = chunk_len & 0xFF;
247
248 if ((err = HID_WriteReport(cmd)))
249 break;
250
251 uint8_t seq = 1;
252
253 do {
254 uint8_t rsp[68];
255 if ((err = HID_ReadReport(rsp)))
256 break;
257
258 const uint8_t r = rsp[0] & COMMAND_MASK;
259
260 if (r == RESPONSE_READ_FLASH_DATA) {
261 if (seq != rsp[1]) {
262 err = LC_ERROR;
263 debug("Invalid sequence %02X %02x",
264 seq, rsp[1]);
265 break;
266 }
267 seq += 0x11;
268 const unsigned int rxlen =
269 rxlenmap[rsp[0] & LENGTH_MASK];
270 if (rxlen) {
271 if (verify) {
272 if (memcmp(pr, rsp+2, rxlen)) {
273 debug("Verify fail");
274 err = LC_ERROR_VERIFY;
275 break;
276 }
277 } else {
278 memcpy(pr, rsp+2, rxlen);
279 }
280 pr += rxlen;
281 addr += rxlen;
282 bytes_read += rxlen;
283 }
284 } else if (r == RESPONSE_DONE) {
285 break;
286 } else {
287 debug("Invalid response [%02X]", rsp[0]);
288 err = LC_ERROR;
289 }
290 } while (err == 0);
291
292 if (cb) {
293 cb(cb_stage, cb_count++, bytes_read, len, LC_CB_COUNTER_TYPE_BYTES,
294 cb_arg, NULL);
295 }
296 } while (err == 0 && addr < end);
297
298 return err;
299 }
300
InvalidateFlash(lc_callback cb,void * cb_arg,uint32_t lc_stage)301 int CRemote::InvalidateFlash(lc_callback cb, void *cb_arg, uint32_t lc_stage)
302 {
303 const uint8_t ivf[64] = { COMMAND_WRITE_MISC | 0x01,
304 COMMAND_MISC_INVALIDATE_FLASH };
305 int err;
306
307 if (cb)
308 cb(lc_stage, 0, 0, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
309
310 if ((err = HID_WriteReport(ivf)))
311 return err;
312
313 if (cb)
314 cb(lc_stage, 1, 1, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
315
316 uint8_t rsp[68];
317 if ((err = HID_ReadReport(rsp)))
318 return err;
319
320 if ((rsp[0] & COMMAND_MASK) != RESPONSE_DONE ||
321 (rsp[1] & COMMAND_MASK) != COMMAND_WRITE_MISC) {
322 return 1;
323 }
324
325 if (cb)
326 cb(lc_stage, 2, 2, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
327
328 return 0;
329 }
330
331
EraseFlash(uint32_t addr,uint32_t len,const TRemoteInfo & ri,lc_callback cb,void * cb_arg,uint32_t cb_stage)332 int CRemote::EraseFlash(uint32_t addr, uint32_t len, const TRemoteInfo &ri,
333 lc_callback cb, void *cb_arg, uint32_t cb_stage)
334 {
335 const unsigned int *sectors = ri.flash->sectors;
336 const unsigned int flash_base = ri.arch->flash_base;
337
338 const uint32_t end = addr + len;
339
340 int err = 0;
341 uint32_t num_sectors = 0;
342
343 uint32_t n = 0;
344
345 // skip to where we need to start writing
346 while (sectors[n] + flash_base < addr) {
347 n++;
348 }
349 // start on the NEXT one
350 n++;
351
352 num_sectors = n;
353 while (sectors[num_sectors] + flash_base < end) {
354 num_sectors++;
355 }
356 num_sectors -= n - 1;
357
358 uint32_t sector_begin = sectors[n-1] + flash_base;
359 uint32_t sector_end = sectors[n] + flash_base;
360
361 for (uint32_t i = 0; i < num_sectors; i++) {
362 static uint8_t erase_cmd[64] = {0};
363 erase_cmd[0] = COMMAND_ERASE_FLASH;
364 erase_cmd[1] = (sector_begin >> 16) & 0xFF;
365 erase_cmd[2] = (sector_begin >> 8) & 0xFF;
366 erase_cmd[3] = sector_begin & 0xFF;
367
368 if ((err = HID_WriteReport(erase_cmd)))
369 break;
370
371 uint8_t rsp[68];
372 if ((err = HID_ReadReport(rsp, 5000)))
373 break;
374
375 if (cb)
376 cb(cb_stage, i, i+1, num_sectors, LC_CB_COUNTER_TYPE_STEPS,
377 cb_arg, NULL);
378 debug("erase sector %2i: %06X - %06X", n, sector_begin, sector_end);
379 sector_begin = sector_end;
380 sector_end = sectors[++n] + flash_base;
381 }
382
383 return err;
384 }
385
PrepFirmware(const TRemoteInfo & ri,lc_callback cb,void * cb_arg,uint32_t cb_stage)386 int CRemote::PrepFirmware(const TRemoteInfo &ri, lc_callback cb, void *cb_arg,
387 uint32_t cb_stage)
388 {
389 int err = 0;
390 uint8_t data[1] = { 0x00 };
391
392 if (cb)
393 cb(cb_stage, 0, 0, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
394
395 if (ri.arch->firmware_update_base == ri.arch->firmware_base) {
396 /*
397 * The preperation for where the staging area IS the config
398 * area.
399 * restart config
400 * write "1" to flash addr 200000
401 */
402 if ((err = WriteMiscByte(0x09, 1, COMMAND_MISC_RESTART_CONFIG, data)))
403 return LC_ERROR;
404
405 if (cb)
406 cb(cb_stage, 1, 1, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
407
408 if ((err = WriteFlash(0x200000, 1, data, ri.protocol, NULL, NULL, 0)))
409 return LC_ERROR;
410 } else {
411 /*
412 * The preperation for where the staging area is distinct.
413 * write "1" to ram addr 0
414 * read it back
415 */
416 if ((err = WriteRam(0, 1, data)))
417 return LC_ERROR_WRITE;
418
419 if (cb)
420 cb(cb_stage, 1, 1, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
421
422 if ((err = ReadRam(0, 1, data)))
423 return LC_ERROR_WRITE;
424 if (data[0] != 0)
425 return LC_ERROR_VERIFY;
426 }
427 if (cb)
428 cb(cb_stage, 2, 2, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
429
430 return 0;
431 }
432
FinishFirmware(const TRemoteInfo & ri,lc_callback cb,void * cb_arg,uint32_t cb_stage)433 int CRemote::FinishFirmware(const TRemoteInfo &ri, lc_callback cb, void *cb_arg,
434 uint32_t cb_stage)
435 {
436 int err = 0;
437
438 if (cb)
439 cb(cb_stage, 0, 0, 3, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
440
441 uint8_t data[1];
442 if (ri.arch->firmware_update_base == ri.arch->firmware_base) {
443 data[0] = 0x02;
444 if ((err = WriteFlash(0x200000, 1, data, ri.protocol, NULL, NULL, 0)))
445 return LC_ERROR;
446 if (cb)
447 cb(cb_stage, 1, 1, 3, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
448 } else {
449 data[0] = 0x02;
450 if ((err = WriteRam(0, 1, data))) {
451 debug("Failed to write 2 to RAM 0");
452 return LC_ERROR_WRITE;
453 }
454 if (cb)
455 cb(cb_stage, 1, 1, 3, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
456 if ((err = ReadRam(0, 1, data))) {
457 debug("Failed to from RAM 0");
458 return LC_ERROR_WRITE;
459 }
460 if (cb)
461 cb(cb_stage, 2, 2, 3, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
462 if (data[0] != 2) {
463 printf("byte is %d\n", data[0]);
464 debug("Finalize byte didn't match");
465 return LC_ERROR_VERIFY;
466 }
467 }
468 if (cb)
469 cb(cb_stage, 3, 3, 3, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
470
471 return 0;
472 }
473
PrepConfig(const TRemoteInfo & ri,lc_callback cb,void * cb_arg,uint32_t cb_stage)474 int CRemote::PrepConfig(const TRemoteInfo &ri, lc_callback cb, void *cb_arg,
475 uint32_t cb_stage)
476 {
477 int err;
478 uint8_t data_zero[1] = { 0x00 };
479
480 if (ri.architecture != 14) {
481 if (cb) {
482 cb(cb_stage, 0, 0, 1, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
483 cb(cb_stage, 1, 1, 1, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
484 }
485 return 0;
486 }
487
488 if (cb)
489 cb(cb_stage, 0, 0, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
490
491 if ((err = WriteMiscByte(0x02, 1, COMMAND_MISC_RESTART_CONFIG, data_zero))) {
492 return err;
493 }
494
495 if (cb)
496 cb(cb_stage, 1, 1, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
497
498 if ((err = WriteMiscByte(0x05, 1, COMMAND_MISC_RESTART_CONFIG, data_zero))) {
499 return err;
500 }
501
502 if (cb)
503 cb(cb_stage, 2, 2, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
504
505 return 0;
506 }
507
FinishConfig(const TRemoteInfo & ri,lc_callback cb,void * cb_arg,uint32_t cb_stage)508 int CRemote::FinishConfig(const TRemoteInfo &ri, lc_callback cb, void *cb_arg,
509 uint32_t cb_stage)
510 {
511 int err;
512 uint8_t data_one[1] = { 0x01 };
513 uint8_t data_zero[1] = { 0x00 };
514
515 if (ri.architecture != 14) {
516 return 0;
517 }
518
519 if (cb)
520 cb(cb_stage, 0, 0, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
521
522 if ((err = WriteMiscByte(0x03, 1, COMMAND_MISC_RESTART_CONFIG, data_one))) {
523 return err;
524 }
525
526 if (cb)
527 cb(cb_stage, 1, 1, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
528
529 if ((err = WriteMiscByte(0x06, 1, COMMAND_MISC_RESTART_CONFIG,
530 data_zero))) {
531 return err;
532 }
533
534 if (cb)
535 cb(cb_stage, 2, 2, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
536
537 return 0;
538 }
539
WriteRam(uint32_t addr,const uint32_t len,uint8_t * wr)540 int CRemote::WriteRam(uint32_t addr, const uint32_t len, uint8_t *wr)
541 {
542 return WriteMiscByte(addr, len, COMMAND_MISC_RAM, wr);
543 }
544
ReadRam(uint32_t addr,const uint32_t len,uint8_t * rd)545 int CRemote::ReadRam(uint32_t addr, const uint32_t len, uint8_t *rd)
546 {
547 return ReadMiscByte(addr, len, COMMAND_MISC_RAM, rd);
548 }
549
WriteFlash(uint32_t addr,const uint32_t len,const uint8_t * wr,unsigned int protocol,lc_callback cb,void * cb_arg,uint32_t cb_stage)550 int CRemote::WriteFlash(uint32_t addr, const uint32_t len, const uint8_t *wr,
551 unsigned int protocol, lc_callback cb, void *cb_arg, uint32_t cb_stage)
552 {
553 uint32_t cb_count = 0;
554 const unsigned int max_chunk_len = protocol == 0 ? 749 : 3150;
555
556 /* mapping of lenghts - see specs/protocol.txt */
557 static const unsigned int txlenmap0[] = { 0x07, 7, 6, 5, 4, 3, 2, 1 };
558 static const unsigned int txlenmapx[] =
559 { 0x0A, 63, 31, 15, 7, 6, 5, 4, 3, 2, 1 };
560 const unsigned int *txlenmap = protocol ? txlenmapx : txlenmap0;
561
562 const uint8_t *pw = wr;
563 const uint32_t end = addr+len;
564 unsigned int bytes_written = 0;
565 int err = 0;
566
567 do {
568 static uint8_t write_setup_cmd[64] = {0};
569 write_setup_cmd[0] = COMMAND_WRITE_FLASH | 0x05;
570 write_setup_cmd[1] = (addr >> 16) & 0xFF;
571 write_setup_cmd[2] = (addr >> 8) & 0xFF;
572 write_setup_cmd[3] = addr & 0xFF;
573 uint32_t chunk_len = end - addr;
574 if (chunk_len > max_chunk_len)
575 chunk_len = max_chunk_len;
576 write_setup_cmd[4] = (chunk_len >> 8) & 0xFF;
577 write_setup_cmd[5] = chunk_len & 0xFF;
578
579 if ((err = HID_WriteReport(write_setup_cmd)))
580 break;
581
582 while (chunk_len) {
583 unsigned int n = txlenmap[0];
584 unsigned int i = 1;
585 while (chunk_len < txlenmap[i]) {
586 ++i;
587 --n;
588 }
589 unsigned int block_len = txlenmap[i];
590 uint8_t wd[64] = {0};
591 wd[0] = COMMAND_WRITE_FLASH_DATA | n;
592 memcpy(wd+1, pw, block_len);
593 HID_WriteReport(wd);
594 pw += block_len;
595 addr += block_len;
596 bytes_written += block_len;
597 chunk_len -= block_len;
598 }
599
600 uint8_t end_cmd[64] = { COMMAND_DONE, COMMAND_WRITE_FLASH };
601 HID_WriteReport(end_cmd);
602
603 uint8_t rsp[68];
604 if ((err = HID_ReadReport(rsp, 5000)))
605 break;
606
607 if (cb) {
608 cb(cb_stage, cb_count++, bytes_written, len,
609 LC_CB_COUNTER_TYPE_BYTES, cb_arg, NULL);
610 }
611 } while (addr < end);
612
613 return err;
614 }
615
ReadMiscByte(uint8_t addr,uint32_t len,uint8_t kind,uint8_t * rd)616 int CRemote::ReadMiscByte(uint8_t addr, uint32_t len, uint8_t kind, uint8_t *rd)
617 {
618 uint8_t rmb[64] = { COMMAND_READ_MISC | 0x02, kind, 0 };
619
620 while (len--) {
621 rmb[2] = addr++;
622
623 int err;
624 if ((err = HID_WriteReport(rmb)))
625 return err;
626
627 uint8_t rsp[68];
628 if ((err = HID_ReadReport(rsp)))
629 return err;
630
631 if (rsp[0] != (RESPONSE_READ_MISC_DATA | 0x02) ||
632 rsp[1] != kind)
633 return 1;
634
635 *rd++ = rsp[2];
636 }
637 return 0;
638 }
639
ReadMiscWord(uint16_t addr,uint32_t len,uint8_t kind,uint16_t * rd)640 int CRemote::ReadMiscWord(uint16_t addr, uint32_t len, uint8_t kind,
641 uint16_t *rd)
642 {
643 uint8_t rmw[64] = { COMMAND_READ_MISC | 0x03, kind, 0, 0 };
644
645 while (len--) {
646 rmw[2] = addr >> 8;
647 rmw[3] = addr & 0xFF;
648 ++addr;
649
650 int err;
651 if ((err = HID_WriteReport(rmw)))
652 return err;
653
654 uint8_t rsp[68];
655 if ((err = HID_ReadReport(rsp)))
656 return err;
657
658 // WARNING: The 880 responds with C2 rather than C3
659 if ((rsp[0] & COMMAND_MASK) != RESPONSE_READ_MISC_DATA ||
660 rsp[1] != kind) {
661 return 1;
662 }
663
664 *rd++ = (rsp[2] << 8) | rsp[3];
665 }
666 return 0;
667 }
668
WriteMiscByte(uint8_t addr,uint32_t len,uint8_t kind,uint8_t * wr)669 int CRemote::WriteMiscByte(uint8_t addr, uint32_t len, uint8_t kind,
670 uint8_t *wr)
671 {
672 uint8_t wmb[64] = {0};
673 wmb[0] = COMMAND_WRITE_MISC | 0x03;
674 wmb[1] = kind;
675
676 while (len--) {
677 wmb[2] = addr++;
678 wmb[3] = *wr++;
679
680 int err;
681 if ((err = HID_WriteReport(wmb)))
682 return err;
683
684 uint8_t rsp[68];
685 if ((err = HID_ReadReport(rsp)))
686 return err;
687 if ((rsp[0] & COMMAND_MASK) != RESPONSE_DONE ||
688 rsp[1] != COMMAND_WRITE_MISC) {
689 return 1;
690 }
691 }
692 return 0;
693 }
694
WriteMiscWord(uint16_t addr,uint32_t len,uint8_t kind,uint16_t * wr)695 int CRemote::WriteMiscWord(uint16_t addr, uint32_t len, uint8_t kind,
696 uint16_t *wr)
697 {
698 uint8_t wmw[64] = {0};
699 wmw[0] = COMMAND_WRITE_MISC | 0x05;
700 wmw[1] = kind;
701
702 while (len--) {
703 wmw[2] = addr >> 8;
704 wmw[3] = addr & 0xFF;
705 ++addr;
706 wmw[4] = *wr >> 8;
707 wmw[5] = *wr & 0xFF;
708 ++wr;
709
710 int err;
711 if ((err = HID_WriteReport(wmw)))
712 return err;
713
714 uint8_t rsp[68];
715 if ((err = HID_ReadReport(rsp)))
716 return err;
717 if ((rsp[0] & COMMAND_MASK) != RESPONSE_DONE ||
718 rsp[1] != COMMAND_WRITE_MISC) {
719 return 1;
720 }
721 }
722 return 0;
723 }
724
725
GetTime(const TRemoteInfo & ri,THarmonyTime & ht)726 int CRemote::GetTime(const TRemoteInfo &ri, THarmonyTime &ht)
727 {
728 int err = 0;
729
730 if (ri.architecture < 8) {
731 uint8_t tsv[8];
732 err = ReadMiscByte(0, 6, COMMAND_MISC_STATE, tsv);
733 ht.second = tsv[0];
734 ht.minute = tsv[1];
735 ht.hour = tsv[2];
736 ht.dow = 7;
737 ht.day = 1 + tsv[3];
738 ht.month = 1 + tsv[4];
739 ht.year = 2000 + tsv[5];
740 } else {
741 uint16_t tsv[8];
742 err = ReadMiscWord(0, 7, COMMAND_MISC_STATE, tsv);
743 ht.second = tsv[0];
744 ht.minute = tsv[1];
745 ht.hour = tsv[2];
746 ht.day = 1 + tsv[3];
747 ht.dow = tsv[4] & 7;
748 ht.month = 1 + tsv[5];
749 ht.year = 2000 + tsv[6];
750 }
751
752 ht.utc_offset = 0;
753 ht.timezone = "";
754
755 return err;
756 }
757
SetTime(const TRemoteInfo & ri,const THarmonyTime & ht,lc_callback cb,void * cb_arg,uint32_t cb_stage)758 int CRemote::SetTime(const TRemoteInfo &ri, const THarmonyTime &ht,
759 lc_callback cb, void *cb_arg, uint32_t cb_stage)
760 {
761 int err = 0;
762 uint8_t rsp[68];
763 int cb_count = 0;
764
765 if (cb)
766 cb(cb_stage, cb_count++, 0, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
767
768 if (ri.architecture < 8) {
769 uint8_t tsv[8];
770 tsv[0] = 0;
771 tsv[1] = ht.minute;
772 tsv[2] = ht.hour;
773 tsv[3] = ht.day - 1;
774 tsv[4] = ht.month -1;
775 tsv[5] = ht.year - 2000;
776 if ((err = WriteMiscByte(0, 6, COMMAND_MISC_STATE, tsv)))
777 return err;
778 if (cb)
779 cb(cb_stage, cb_count++, 1, 3, LC_CB_COUNTER_TYPE_STEPS, cb_arg,
780 NULL);
781
782 tsv[0] = ht.second;
783 err = WriteMiscByte(0, 1, COMMAND_MISC_STATE, tsv);
784 } else {
785 uint16_t tsv[8];
786 tsv[0] = 0;
787 tsv[1] = ht.minute;
788 tsv[2] = ht.hour;
789 tsv[3] = ht.day-1;
790 tsv[4] = ht.dow;
791 tsv[5] = ht.month-1;
792 tsv[6] = ht.year-2000;
793 if ((err = WriteMiscWord(0, 7, COMMAND_MISC_STATE, tsv)))
794 return err;
795 if (cb)
796 cb(cb_stage, cb_count++, 1, 3, LC_CB_COUNTER_TYPE_STEPS, cb_arg,
797 NULL);
798 tsv[0] = ht.second;
799 if ((err = WriteMiscWord(0, 1, COMMAND_MISC_STATE, tsv)))
800 return err;
801
802 // Send Recalc Clock command for 880 only (not 360/520/550)
803 if (ri.architecture == 8) {
804 static const uint8_t rcc[64] = {
805 COMMAND_WRITE_MISC | 0x01,
806 COMMAND_MISC_CLOCK_RECALCULATE };
807 err = HID_WriteReport(rcc);
808 }
809 }
810 if (cb)
811 cb(cb_stage, cb_count++, 2, 3, LC_CB_COUNTER_TYPE_STEPS, cb_arg,
812 NULL);
813
814 if (err != 0) {
815 return err;
816 }
817
818 /*
819 * For some models, they return a RESPONSE_DONE, and we have to read
820 * it otherwise otherwise next command will fail.
821 * However, other devices don't return this, in which case the read
822 * failes.
823 * So, if the read succeeds, we check the response, otherwise we just
824 * move on with life.
825 */
826 err = HID_ReadReport(rsp);
827 if (err == 0) {
828 if ((rsp[0] & COMMAND_MASK) != RESPONSE_DONE) {
829 err = 1;
830 }
831 } else {
832 err = 0;
833 }
834 if (cb)
835 cb(cb_stage, cb_count++, 3, 3, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
836
837 return err;
838 }
839
check_seq(int received_seq,uint8_t & expected_seq)840 bool check_seq(int received_seq, uint8_t &expected_seq)
841 {
842 if (received_seq == expected_seq) {
843 return true;
844 }
845
846 if (received_seq == 0x1f && expected_seq == 0x10) {
847 /*
848 * Handle 880 SNAFU
849 *
850 * Does this indicate a bad learn???
851 * Needs more testing
852 */
853 debug("sequence glitch!");
854 expected_seq += 0x0F;
855 return true;
856 } else {
857 debug("\nInvalid sequence %02X %02x", expected_seq,
858 received_seq);
859 return false;
860 }
861 }
862
_handle_ir_response(uint8_t rsp[64],uint32_t & ir_word,uint32_t & t_on,uint32_t & t_off,uint32_t & t_total,uint32_t & ir_count,uint32_t * & ir_signal,uint32_t & freq)863 int _handle_ir_response(uint8_t rsp[64], uint32_t &ir_word, uint32_t &t_on,
864 uint32_t &t_off, uint32_t &t_total, uint32_t &ir_count,
865 uint32_t *&ir_signal, uint32_t &freq)
866 {
867 const uint32_t len = rsp[63];
868 if ((len & 1) != 0) {
869 return 3; // Invalid length
870 }
871
872 for (uint32_t u = 2; u < len; u += 2) {
873 const uint32_t t = rsp[u] << 8 | rsp[1+u];
874 if (ir_word > 2) {
875 /*
876 * For ODD words, t is the total time, we'll
877 * update the total OFF time and be done.
878 *
879 * For EVEN words, t is just the ON time
880 * -- IF we have any ON time, then we go ahead
881 * and record the off and on times we should
882 * have now gathered.
883 *
884 * Why do we differentiate between even/odd?
885 * Perhaps just to make sure we've had two
886 * cycles, and thus have off/on?
887 */
888 if (ir_word & 1) {
889 // t == on + off time
890 if (t_on) {
891 t_off = t - t_on;
892 } else {
893 t_off += t;
894 }
895 } else {
896 // t == on time
897 t_on = t;
898 if (t_on) {
899 debug("-%i\n", t_off);
900 if (ir_count < MAX_IR_SIGNAL_LENGTH) {
901 ir_signal[ir_count++] = t_off;
902 }
903 debug("+%i\n", t_on);
904 if (ir_count < MAX_IR_SIGNAL_LENGTH) {
905 ir_signal[ir_count++] = t_on;
906 }
907 t_total += t_off + t_on;
908 }
909 }
910 } else {
911 /*
912 * For the first 3 words...
913 * the first one, we ignore, apparently
914 * the second one, we start keeping track of ON time
915 * the third one, we have enough data to calculate the
916 * frequency and record the on time we've calculated
917 */
918 switch (ir_word) {
919 case 0: // ???
920 break;
921 case 1: // on time of first burst
922 t_on = t;
923 break;
924 case 2: // carrier cycle count of first burst
925 if (t_on) {
926 freq = static_cast<uint32_t>(
927 static_cast<uint64_t>(t)
928 *1000000/(t_on));
929 debug("%i Hz", freq);
930 debug("+%i", t_on);
931 ir_signal[ir_count++] = t_on;
932 }
933 break;
934 }
935 }
936 ++ir_word;
937 }
938 return 0;
939 }
940
941 // This section of IR learning code is common between pure HID and MH remotes.
942 // 'seq' is the starting sequence number, which differs between HID and MH.
LearnIRInnerLoop(uint32_t * freq,uint32_t ** ir_signal,uint32_t * ir_signal_length,uint8_t seq)943 int LearnIRInnerLoop(uint32_t *freq, uint32_t **ir_signal,
944 uint32_t *ir_signal_length, uint8_t seq)
945 {
946 int err = 0;
947 uint8_t rsp[68];
948
949 // Count of how man IR words we've received.
950 uint32_t ir_word = 0;
951 // Time button is on and off
952 uint32_t t_on = 0;
953 uint32_t t_off = 0;
954 // total duration of received signal:
955 // abort when > MAX_IR_SIGNAL_DURATION
956 uint32_t t_total = 0;
957
958 *ir_signal_length = 0;
959 *ir_signal = new uint32_t[MAX_IR_SIGNAL_LENGTH];
960 /*
961 * Caller is responsible for deallocation of *ir_signal after use.
962 *
963 * Loop while we haven not:
964 * - any error (including signal duration and buffer overflow)
965 * - signal interrupted for IR_LEARN_DONE_TIMEOUT or longer
966 */
967 while ((err == 0) && (t_off < IR_LEARN_DONE_TIMEOUT * 1000)) {
968 if ((err = HID_ReadReport(rsp, ir_word ?
969 IR_LEARN_DONE_TIMEOUT : IR_LEARN_START_TIMEOUT))) {
970 err = LC_ERROR_READ;
971 break;
972 }
973 const uint8_t r = rsp[0] & COMMAND_MASK;
974 if (r == RESPONSE_IRCAP_DATA) {
975 if (!check_seq(rsp[1], seq)) {
976 err = LC_ERROR;
977 break;
978 }
979 seq += 0x10;
980 /*
981 * This will handle the IR response including updating
982 * t_off so we can exit the loop if long enough time
983 * goes by without action.
984 */
985 err = _handle_ir_response(rsp, ir_word, t_on, t_off,
986 t_total, *ir_signal_length, *ir_signal, *freq);
987 if (err != 0) {
988 break;
989 }
990 } else if (r == RESPONSE_DONE) {
991 break;
992 } else {
993 debug("Invalid response [%02X]", rsp[1]);
994 err = LC_ERROR;
995 }
996 /* check for overflow: */
997 if ((t_total > MAX_IR_SIGNAL_DURATION * 1000)
998 || (*ir_signal_length > MAX_IR_SIGNAL_LENGTH)) {
999 err = LC_ERROR_IR_OVERFLOW;
1000 }
1001 }
1002
1003 if ((err == 0) && (*ir_signal_length > 0)) {
1004 /* we have actually got some signal */
1005 if (t_off) {
1006 debug("-%i", t_off);
1007 }
1008 /* make sure we record a final off */
1009 if (*ir_signal_length < MAX_IR_SIGNAL_LENGTH) {
1010 (*ir_signal)[(*ir_signal_length)++] = t_off;
1011 }
1012 }
1013
1014 return err;
1015 }
1016
LearnIR(uint32_t * freq,uint32_t ** ir_signal,uint32_t * ir_signal_length,lc_callback cb,void * cb_arg,uint32_t cb_stage)1017 int CRemote::LearnIR(uint32_t *freq, uint32_t **ir_signal,
1018 uint32_t *ir_signal_length, lc_callback cb, void *cb_arg,
1019 uint32_t cb_stage)
1020 {
1021 int err = 0;
1022 uint8_t rsp[68];
1023
1024 static const uint8_t start_ir_learn[64] = { COMMAND_START_IRCAP };
1025 static const uint8_t stop_ir_learn[64] = { COMMAND_STOP_IRCAP };
1026
1027 if (cb) {
1028 cb(cb_stage, 0, 0, 1, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
1029 }
1030
1031 if (HID_WriteReport(start_ir_learn) != 0) {
1032 return LC_ERROR_WRITE;
1033 }
1034
1035 err = LearnIRInnerLoop(freq, ir_signal, ir_signal_length, 0);
1036
1037 if (HID_WriteReport(stop_ir_learn) != 0) {
1038 err = LC_ERROR_WRITE;
1039 }
1040
1041 /* flush HID buffer until empty or RESPONSE_DONE: */
1042 do {
1043 if (HID_ReadReport(rsp, IR_LEARN_DONE_TIMEOUT) != 0) {
1044 err = LC_ERROR_READ;
1045 break;
1046 }
1047 } while ((rsp[0] & COMMAND_MASK) != RESPONSE_DONE);
1048
1049 if (cb && !err) {
1050 cb(cb_stage, 1, 1, 1, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
1051 }
1052
1053 return err;
1054 }
1055
ReadFile(const char * filename,uint8_t * rd,const uint32_t rdlen,uint32_t * data_read,uint8_t start_seq,lc_callback cb,void * cb_arg,uint32_t cb_stage)1056 int CRemote::ReadFile(const char *filename, uint8_t *rd, const uint32_t rdlen,
1057 uint32_t *data_read, uint8_t start_seq, lc_callback cb,
1058 void *cb_arg, uint32_t cb_stage)
1059 {
1060 return LC_ERROR_UNSUPP;
1061 }
1062
WriteFile(const char * filename,uint8_t * wr,const uint32_t wrlen)1063 int CRemote::WriteFile(const char *filename, uint8_t *wr, const uint32_t wrlen)
1064 {
1065 return LC_ERROR_UNSUPP;
1066 }
1067