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 2010
20 */
21
22 #include <string.h>
23 #include "libconcord.h"
24 #include "lc_internal.h"
25 #include "hid.h"
26 #include "remote.h"
27 #include "usblan.h"
28 #include "protocol_z.h"
29
30 /* Have we acked the syn packet yet? */
31 static bool SYN_ACKED = false;
32 static unsigned int last_seq;
33 static unsigned int last_ack;
34 static unsigned int last_payload_bytes;
35
TCP_Ack(bool increment_ack=false,bool fin=false)36 int TCP_Ack(bool increment_ack = false, bool fin = false) {
37 uint8_t pkt[HID_UDP_MAX_PACKET_SIZE];
38
39 /*
40 * Note: It's the caller's responsibility to ensure we've already
41 * seen the SYN packet.
42 */
43
44 uint8_t seq;
45 uint8_t ack;
46 uint8_t flags;
47
48 seq = last_ack;
49 ack = last_seq + last_payload_bytes;
50 if (increment_ack)
51 ack++;
52 flags = TYPE_TCP_ACK;
53 if (fin)
54 flags |= TYPE_TCP_FIN;
55 pkt[0] = 3;
56 pkt[1] = flags;
57 pkt[2] = seq;
58 pkt[3] = ack;
59
60 debug("Writing packet:");
61 #ifdef _DEBUG
62 for (int i = 0; i <= pkt[0]; i++) {
63 fprintf(stderr, "%02X ", pkt[i]);
64 }
65 fprintf(stderr, "\n");
66 #endif
67
68 return HID_WriteReport(pkt);
69 }
70
71
72 /*
73 * The HID-based zwave remotes have two modes: so called "UDP" and "TCP". Do
74 * not confuse these with the network protocols of similar names.
75 *
76 * The non-HID based zwave remotes only use "TCP".
77 *
78 * For more information on the various Read/Write functions here and how they
79 * fit together, see the specs/protocol_z.txt file.
80 */
UDP_Write(uint8_t typ,uint8_t cmd,uint32_t len,uint8_t * data)81 int CRemoteZ_HID::UDP_Write(uint8_t typ, uint8_t cmd, uint32_t len,
82 uint8_t *data)
83 {
84 if (len > 60)
85 return LC_ERROR;
86 uint8_t pkt[HID_UDP_MAX_PACKET_SIZE];
87 pkt[0] = 3+len;
88 pkt[1] = 1; // UDP
89 pkt[2] = typ;
90 pkt[3] = 0xFF & cmd;
91 if (data && len)
92 memcpy(pkt + 4, data, len);
93
94 debug("Writing packet:");
95 #ifdef _DEBUG
96 for (int i = 0; i <= pkt[0]; i++) {
97 fprintf(stderr, "%02X ", pkt[i]);
98 }
99 fprintf(stderr, "\n");
100 #endif
101
102 return HID_WriteReport(pkt);
103 }
104
UDP_Read(uint8_t & status,uint32_t & len,uint8_t * data)105 int CRemoteZ_HID::UDP_Read(uint8_t &status, uint32_t &len, uint8_t *data)
106 {
107 uint8_t pkt[HID_UDP_MAX_PACKET_SIZE];
108 int err;
109 if ((err = HID_ReadReport(pkt))) {
110 return LC_ERROR_READ;
111 }
112
113 debug("Reading packet:");
114 #ifdef _DEBUG
115 for (int i = 0; i <= pkt[0]; i++) {
116 fprintf(stderr, "%02X ", pkt[i]);
117 }
118 fprintf(stderr, "\n");
119 #endif
120
121 if (pkt[0] < 4) {
122 return LC_ERROR;
123 }
124 if (pkt[0] > 4) {
125 status = pkt[4];
126 }
127 len = pkt[0] - 4;
128 /*
129 * pkt[0] is the index of the last byte, which means it is equal to the
130 * length of the packet minus one byte. We want to copy everything but the
131 * first byte, so we copy pkt[0] bytes.
132 */
133 memcpy(data, pkt + 1, pkt[0]);
134 return 0;
135 }
136
TCP_Write(uint8_t typ,uint8_t cmd,uint32_t len,uint8_t * data)137 int CRemoteZ_HID::TCP_Write(uint8_t typ, uint8_t cmd, uint32_t len,
138 uint8_t *data)
139 {
140 uint8_t pkt[HID_UDP_MAX_PACKET_SIZE];
141
142 /*
143 * Note: It's the caller's responsibility to ensure we've already
144 * seen the SYN packet.
145 */
146
147 uint8_t seq;
148 uint8_t ack;
149 uint8_t flags;
150
151 if (!SYN_ACKED) {
152 seq = 0x28;
153 ack = last_seq + 1;
154 flags = TYPE_TCP_ACK | TYPE_TCP_SYN;
155 SYN_ACKED = true;
156 } else {
157 seq = last_ack;
158 ack = last_seq + last_payload_bytes;
159 flags = TYPE_TCP_ACK;
160 }
161
162 if (len > 60)
163 return LC_ERROR;
164 pkt[0] = 5+len;
165 pkt[1] = flags;
166 pkt[2] = seq;
167 pkt[3] = ack;
168 pkt[4] = typ;
169 pkt[5] = cmd;
170 if (data && len)
171 memcpy(pkt + 6, data, len);
172
173 debug("Writing packet:");
174 #ifdef _DEBUG
175 for (int i = 0; i <= pkt[0]; i++) {
176 fprintf(stderr, "%02X ", pkt[i]);
177 }
178 fprintf(stderr, "\n");
179 #endif
180
181 return HID_WriteReport(pkt);
182 }
183
184
TCP_Read(uint8_t & status,uint32_t & len,uint8_t * data)185 int CRemoteZ_HID::TCP_Read(uint8_t &status, uint32_t &len, uint8_t *data)
186 {
187 uint8_t pkt[HID_UDP_MAX_PACKET_SIZE];
188 int err;
189 /*
190 * Many TCP operations can take a while, like computing checksums,
191 * and it will be a while before we get a response. So we set the
192 * timeout to 30 seconds.
193 */
194 if ((err = HID_ReadReport(pkt, 30000))) {
195 return LC_ERROR_READ;
196 }
197
198 debug("Reading packet:");
199 #ifdef _DEBUG
200 for (int i = 0; i <= pkt[0]; i++) {
201 fprintf(stderr, "%02X ", pkt[i]);
202 }
203 fprintf(stderr, "\n");
204 #endif
205
206 if (pkt[0] < 3) {
207 return LC_ERROR;
208 }
209 /*
210 * pkt[0] is the index of the last byte, which means it is equal to the
211 * length of packet minus one byte. 'len' is expected to be set to the
212 * payload size. To get the payload size we subtract both the TCP and
213 * UDP headers from pkt[0] and then add one.
214 */
215 len = pkt[0] - HID_TCP_HDR_SIZE - HID_UDP_HDR_SIZE + 1;
216 last_seq = pkt[2];
217 last_ack = pkt[3];
218 last_payload_bytes = len + HID_UDP_HDR_SIZE; // tcp payload size
219 //if(!len) return 0;
220 //memcpy(data, pkt + 6, len);
221 // include headers, minus the size
222 memcpy(data, pkt + 1, len + HID_TCP_HDR_SIZE + HID_UDP_HDR_SIZE - 1);
223 return 0;
224 }
225
Write(uint8_t typ,uint8_t cmd,uint32_t len,uint8_t * data)226 int CRemoteZ_HID::Write(uint8_t typ, uint8_t cmd, uint32_t len, uint8_t *data)
227 {
228 return UDP_Write(typ, cmd, len, data);
229 }
230
Read(uint8_t & status,uint32_t & len,uint8_t * data)231 int CRemoteZ_HID::Read(uint8_t &status, uint32_t &len, uint8_t *data)
232 {
233 return UDP_Read(status, len, data);
234 }
235
ParseParams(uint32_t len,uint8_t * data,TParamList & pl)236 int CRemoteZ_HID::ParseParams(uint32_t len, uint8_t *data, TParamList &pl)
237 {
238 debug("ParseParams, %02x %02x %02x %02x %02x %02x\n", data[0], data[1],
239 data[2], data[3], data[4], data[5]);
240 switch (data[2]) {
241 case COMMAND_GET_SYSTEM_INFO:
242 pl.count = 8;
243 pl.p[0] = data + 4;
244 pl.p[1] = data + 6;
245 pl.p[2] = data + 8;
246 pl.p[3] = data + 10;
247 pl.p[4] = data + 12;
248 pl.p[5] = data + 14;
249 pl.p[6] = data + 15;
250 pl.p[7] = data + 17;
251 break;
252 case COMMAND_GET_CURRENT_TIME:
253 pl.count = (len > 16) ? 12 : 8;
254 pl.p[0] = data + 4;
255 pl.p[1] = data + 6;
256 pl.p[2] = data + 7;
257 pl.p[3] = data + 8;
258 pl.p[4] = data + 9;
259 pl.p[5] = data + 10;
260 pl.p[6] = data + 11;
261 pl.p[7] = data + 12;
262 pl.p[8] = data + 14;
263 pl.p[9] = data + 16;
264 pl.p[10] = data + 18;
265 pl.p[11] = data + 20;
266 break;
267 case COMMAND_GET_GUID:
268 pl.count = 1;
269 pl.p[0] = data + 4;
270 break;
271 }
272 return 0;
273 }
274
275
Write(uint8_t typ,uint8_t cmd,uint32_t len,uint8_t * data)276 int CRemoteZ_USBNET::Write(uint8_t typ, uint8_t cmd, uint32_t len,
277 uint8_t *data)
278 {
279 if (len > USBNET_MAX_PACKET_SIZE) {
280 return LC_ERROR;
281 }
282
283 static const uint8_t service_type = SERVICE_FAMILY_CLIENT;
284 const bool request = (typ == TYPE_REQUEST);
285 const uint8_t status = STATUS_OK;
286
287 uint8_t pkt[USBNET_MAX_PACKET_SIZE+3]; /* add standard 3-byte header */
288 pkt[0] = (service_type << 4) | ((cmd >> 8) & 0x0F);
289 pkt[1] = cmd & 0xFF;
290 pkt[2] = request ? 0x80 : (status & 0x7F);
291
292 if (len && data) {
293 memcpy(pkt + 3, data, len);
294 len += 3;
295 } else {
296 pkt[3] = 0x00; // Param count
297 len = 4;
298 }
299
300 return UsbLan_Write(len, pkt);
301 }
302
Read(uint8_t & status,uint32_t & len,uint8_t * data)303 int CRemoteZ_USBNET::Read(uint8_t &status, uint32_t &len, uint8_t *data)
304 {
305 uint8_t buf[1600];
306 len = sizeof(buf);
307 int err;
308 if ((err = UsbLan_Read(len, buf)))
309 return err;
310
311 memcpy(data, buf, len);
312
313 return err;
314 }
315
ParseParams(uint32_t len,uint8_t * data,TParamList & pl)316 int CRemoteZ_USBNET::ParseParams(uint32_t len, uint8_t *data, TParamList &pl)
317 {
318 unsigned int n = 0;
319 unsigned int i = 4;
320 while (i < len) {
321 unsigned int param_len = data[i];
322 switch (param_len & 0xC0) {
323 case 0x00:
324 case 0x80:
325 param_len &= 0x3F;
326 break;
327 case 0x40:
328 param_len = (param_len & 0x3F) * 4;
329 break;
330 case 0xC0:
331 param_len = (param_len & 0x3F) * 512;
332 break;
333 }
334 ++i;
335 pl.p[n++] = data+i;
336 #ifdef _DEBUG
337 fprintf(stderr, "DEBUG (%s): %3i:", __FUNCTION__, param_len);
338 for(unsigned int j = 0; j < param_len; ++j)
339 fprintf(stderr, " %02X", data[i+j]);
340 fprintf(stderr, "\n");
341 #endif
342 i += param_len;
343 }
344
345 //data[3]; // Number of parameters
346 pl.count = n;
347
348 return 0;
349 }
350
TCPSendAndCheck(uint8_t cmd,uint32_t len,uint8_t * data)351 int CRemoteZ_USBNET::TCPSendAndCheck(uint8_t cmd, uint32_t len, uint8_t *data)
352 {
353 int err = 0;
354 uint8_t status;
355 unsigned int rlen;
356 uint8_t rsp[60];
357
358 if ((err = Write(TYPE_REQUEST, cmd, len, data))) {
359 debug("Failed to send request %02X", cmd);
360 return LC_ERROR_WRITE;
361 }
362
363 if ((err = Read(status, rlen, rsp))) {
364 debug("Failed to read from remote");
365 return LC_ERROR_READ;
366 }
367
368 if (rsp[2] != TYPE_RESPONSE) {
369 debug("Packet didn't have response bit!");
370 return LC_ERROR;
371 }
372
373 if (rsp[1] != cmd) {
374 debug("The cmd bit didn't match our request packet");
375 return LC_ERROR;
376 }
377
378 return 0;
379 }
380
UpdateConfig(const uint32_t len,const uint8_t * wr,lc_callback cb,void * cb_arg,uint32_t cb_stage,uint32_t xml_size,uint8_t * xml)381 int CRemoteZ_USBNET::UpdateConfig(const uint32_t len, const uint8_t *wr,
382 lc_callback cb, void *cb_arg,
383 uint32_t cb_stage, uint32_t xml_size,
384 uint8_t *xml)
385 {
386 int err = 0;
387 int cb_count = 0;
388
389 cb(LC_CB_STAGE_INITIALIZE_UPDATE, cb_count++, 0, 2,
390 LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
391
392 /* ACK it with a command to start an update */
393 debug("START_UPDATE");
394 // 2 parameters, each 1 byte.
395 // 1st parameter "flags" seems to always be 0.
396 uint8_t cmd[60] = { 0x02, 0x01, 0x00, 0x01, REGION_USER_CONFIG };
397 if ((err = TCPSendAndCheck(COMMAND_START_UPDATE, 5, cmd))) {
398 return err;
399 }
400
401 cb(LC_CB_STAGE_INITIALIZE_UPDATE, cb_count++, 1, 2,
402 LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
403
404 /* write update-header */
405 debug("UPDATE_HEADER");
406 cmd[0] = 0x02; // 2 parameters
407 cmd[1] = 0x04; // 1st parameter 4 bytes (size)
408 cmd[2] = (len & 0xFF000000) >> 24;
409 cmd[3] = (len & 0x00FF0000) >> 16;
410 cmd[4] = (len & 0x0000FF00) >> 8;
411 cmd[5] = (len & 0x000000FF);
412 cmd[6] = 0x01; // 2nd parameter 1 byte (region id)
413 cmd[7] = REGION_USER_CONFIG;
414 if ((err = TCPSendAndCheck(COMMAND_WRITE_UPDATE_HEADER, 8, cmd))) {
415 return err;
416 }
417
418 cb(LC_CB_STAGE_INITIALIZE_UPDATE, cb_count++, 2, 2,
419 LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
420 cb_count = 0;
421
422 /* write data */
423 debug("UPDATE_DATA");
424 uint32_t pkt_len;
425 uint32_t tlen = len;
426 uint8_t *wr_ptr = const_cast<uint8_t*>(wr);
427 uint8_t tmp_pkt[1033];
428 tmp_pkt[0] = 0x03; // 3 parameters
429 tmp_pkt[1] = 0x01; // 1st parameter, 1 byte (region id)
430 tmp_pkt[2] = REGION_USER_CONFIG;
431 tmp_pkt[3] = 0xC2; // 2nd parameter, 1024 bytes (data)
432 tmp_pkt[1028] = 0x04; // 3rd parameter, 4 bytes (length)
433 while (tlen) {
434 pkt_len = 1024; // max packet length seems to be 1024
435 if (tlen < pkt_len) {
436 pkt_len = tlen;
437 }
438 tlen -= pkt_len;
439
440 memcpy(&tmp_pkt[4], wr_ptr, pkt_len);
441 tmp_pkt[1029] = (pkt_len & 0xFF000000) >> 24;
442 tmp_pkt[1030] = (pkt_len & 0x00FF0000) >> 16;
443 tmp_pkt[1031] = (pkt_len & 0x0000FF00) >> 8;
444 tmp_pkt[1032] = (pkt_len & 0x000000FF);
445
446 debug("DATA %d, sending %d bytes, %d bytes left", cb_count,
447 pkt_len, tlen);
448
449 if ((err = TCPSendAndCheck(COMMAND_WRITE_UPDATE_DATA, 1033, tmp_pkt))) {
450 return err;
451 }
452 wr_ptr += pkt_len;
453
454 if (cb) {
455 cb(LC_CB_STAGE_WRITE_CONFIG, cb_count++, (int)(wr_ptr - wr), len,
456 LC_CB_COUNTER_TYPE_BYTES, cb_arg, NULL);
457 }
458 }
459
460 /* write update-done */
461 cb_count = 0;
462 cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 0, 3, LC_CB_COUNTER_TYPE_STEPS,
463 cb_arg, NULL);
464
465 debug("UPDATE_DATA_DONE");
466 cmd[0] = 0x01; // 1 parameter
467 cmd[1] = 0x01; // 1st parameter 1 byte (region id)
468 cmd[2] = REGION_USER_CONFIG;
469 if ((err = TCPSendAndCheck(COMMAND_WRITE_UPDATE_DATA_DONE, 3, cmd))) {
470 return err;
471 }
472
473 cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 1, 3, LC_CB_COUNTER_TYPE_STEPS,
474 cb_arg, NULL);
475
476 /* send get-cheksum */
477 debug("GET_CHECKSUM");
478 cmd[0] = 0x02; // 2 parameters
479 cmd[1] = 0x02; // 1st parameter 2 bytes (seed)
480 cmd[2] = 0xFF; // seed seems to always be FF
481 cmd[3] = 0xFF; // seed seems to always be FF
482 cmd[4] = 0x01; // 2nd parameter 1 byte (region id)
483 cmd[5] = REGION_USER_CONFIG;
484 if ((err = TCPSendAndCheck(COMMAND_GET_UPDATE_CHECKSUM, 6, cmd))) {
485 return err;
486 }
487
488 cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 2, 3, LC_CB_COUNTER_TYPE_STEPS,
489 cb_arg, NULL);
490
491 /* send finish-update */
492 debug("FINISH_UPDATE");
493 cmd[0] = 0x02; // 2 parameters
494 cmd[1] = 0x01; // 1st parameter 1 byte (validate)
495 cmd[2] = 0x01; // validate?
496 cmd[3] = 0x01; // 2nd parameter 1 byte (region id)
497 cmd[4] = REGION_USER_CONFIG;
498 if ((err = TCPSendAndCheck(COMMAND_FINISH_UPDATE, 5, cmd))) {
499 return err;
500 }
501
502 cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 3, 3, LC_CB_COUNTER_TYPE_STEPS,
503 cb_arg, NULL);
504
505 return 0;
506 }
507
GetTime(const TRemoteInfo & ri,THarmonyTime & ht)508 int CRemoteZ_USBNET::GetTime(const TRemoteInfo &ri, THarmonyTime &ht)
509 {
510 int err = 0;
511 if ((err = Write(TYPE_REQUEST, COMMAND_GET_CURRENT_TIME))) {
512 debug("Failed to write to remote");
513 return LC_ERROR_WRITE;
514 }
515 uint8_t time[60];
516 unsigned int len;
517 uint8_t status;
518 if ((err = Read(status, len, time))) {
519 debug("Failed to read to remote");
520 return LC_ERROR_READ;
521 }
522
523 if (time[2] != TYPE_RESPONSE || time[1] != COMMAND_GET_CURRENT_TIME) {
524 debug("Incorrect response type from Get Time");
525 return LC_ERROR_INVALID_DATA_FROM_REMOTE;
526 }
527
528 CRemoteZ_Base::TParamList pl;
529 ParseParams(len, time, pl);
530
531 ht.year = GetWord(pl.p[0]);
532 ht.month = *pl.p[1];
533 ht.day = *pl.p[2];
534 ht.hour = *pl.p[3];
535 ht.minute = *pl.p[4];
536 ht.second = *pl.p[5];
537 ht.dow = *pl.p[6]&7;
538 ht.utc_offset = static_cast<int16_t>(GetWord(pl.p[7]));
539 if (pl.count > 11) {
540 ht.timezone = reinterpret_cast<char*>(pl.p[11]);
541 ht.timezone[3] = '\0';
542 } else {
543 ht.timezone = "";
544 }
545
546 return 0;
547 }
548
SetTime(const TRemoteInfo & ri,const THarmonyTime & ht,lc_callback cb,void * cb_arg,uint32_t cb_stage)549 int CRemoteZ_USBNET::SetTime(const TRemoteInfo &ri, const THarmonyTime &ht,
550 lc_callback cb, void *cb_arg, uint32_t cb_stage)
551 {
552 debug("SetTime");
553 int err = 0;
554
555 uint8_t tsv[32] = {
556 0x0C, // 12 parameters
557 0x02, static_cast<uint8_t>(ht.year >> 8),
558 static_cast<uint8_t>(ht.year), // 2 bytes
559 0x01, static_cast<uint8_t>(ht.month),
560 0x01, static_cast<uint8_t>(ht.day),
561 0x01, static_cast<uint8_t>(ht.hour),
562 0x01, static_cast<uint8_t>(ht.minute),
563 0x01, static_cast<uint8_t>(ht.second),
564 0x01, static_cast<uint8_t>(ht.dow),
565 // utcOffset
566 0x02, 0, 0, // 2 bytes - 900 doesn't seem to accept this
567 // 0s
568 0x02, 0, 0, // 2 bytes
569 0x02, 0, 0, // 2 bytes
570 0x02, 0, 0, // 2 bytes
571 0x83, 0, 0, 0 // 900 doesn't seem to accept this, don't bother
572 };
573
574 if ((err = TCPSendAndCheck(COMMAND_UPDATE_TIME, 32, tsv))) {
575 debug("Failed to write to remote");
576 return err;
577 }
578
579 return 0;
580 }
581
LearnIR(uint32_t * freq,uint32_t ** ir_signal,uint32_t * ir_signal_length,lc_callback cb,void * cb_arg,uint32_t cb_stage)582 int CRemoteZ_USBNET::LearnIR(uint32_t *freq, uint32_t **ir_signal,
583 uint32_t *ir_signal_length, lc_callback cb,
584 void *cb_arg, uint32_t cb_stage)
585 {
586 return LC_ERROR_UNSUPP;
587 }
588
ReadRegion(uint8_t region,uint32_t & rgn_len,uint8_t * rd,lc_callback cb,void * cb_arg,uint32_t cb_stage)589 int CRemoteZ_USBNET::ReadRegion(uint8_t region, uint32_t &rgn_len, uint8_t *rd,
590 lc_callback cb, void *cb_arg, uint32_t cb_stage)
591 {
592 int err = 0;
593 int cb_count = 0;
594 uint8_t rsp[60];
595 unsigned int rlen;
596 uint8_t status;
597 CRemoteZ_Base::TParamList pl;
598
599 debug("READ_REGION");
600 // 1 parameters, 1 byte, region to read.
601 uint8_t cmd[60] = { 0x01, 0x01, region };
602 if ((err = Write(TYPE_REQUEST, COMMAND_READ_REGION, 3, cmd))) {
603 debug("Failed to write to remote");
604 return LC_ERROR_WRITE;
605 }
606 if ((err = Read(status, rlen, rsp))) {
607 debug("Failed to read to remote");
608 return LC_ERROR_READ;
609 }
610 if (rsp[2] != TYPE_RESPONSE || rsp[1] != COMMAND_READ_REGION || rlen != 9 ||
611 rsp[4] != 0x04) {
612 debug("Incorrect response type from remote");
613 return LC_ERROR_INVALID_DATA_FROM_REMOTE;
614 }
615 ParseParams(rlen, rsp, pl);
616 rgn_len = GetWord32(pl.p[0]);
617
618 debug("READ_REGION_DATA");
619 uint32_t pkt_len;
620 unsigned int data_to_read = rgn_len;
621 uint8_t *rd_ptr = rd;
622 uint8_t tmp_pkt[USBNET_MAX_PACKET_SIZE];
623 cmd[0] = 0x01; // 1 parameter
624 cmd[1] = 0x01; // 1st parameter, 1 byte (region id)
625 cmd[2] = region;
626
627 while (data_to_read) {
628 if ((err = Write(TYPE_REQUEST, COMMAND_READ_REGION_DATA, 3, cmd))) {
629 debug("Failed to write to remote");
630 return LC_ERROR_WRITE;
631 }
632 if ((err = Read(status, rlen, tmp_pkt))) {
633 debug("Failed to read to remote");
634 return LC_ERROR_READ;
635 }
636 if (tmp_pkt[2] != TYPE_RESPONSE ||
637 tmp_pkt[1] != COMMAND_READ_REGION_DATA) {
638 debug("Incorrect response type from remote");
639 return LC_ERROR_INVALID_DATA_FROM_REMOTE;
640 }
641 ParseParams(rlen, tmp_pkt, pl);
642 pkt_len = GetWord32(pl.p[2]);
643 data_to_read -= pkt_len;
644
645 if (rd) {
646 memcpy(rd_ptr, pl.p[1], pkt_len);
647 rd_ptr += pkt_len;
648 }
649
650 debug("DATA %d, read %d bytes, %d bytes left", cb_count, pkt_len,
651 data_to_read);
652
653 if (cb) {
654 cb(cb_stage, cb_count++, rgn_len - data_to_read, rgn_len,
655 LC_CB_COUNTER_TYPE_BYTES, cb_arg, NULL);
656 }
657 }
658
659 debug("READ_REGION_DONE");
660 // 1 parameter, 1 byte, region to read.
661 cmd[0] = 0x01;
662 cmd[1] = 0x01;
663 cmd[2] = region;
664 if ((err = TCPSendAndCheck(COMMAND_READ_REGION_DONE, 3, cmd))) {
665 return err;
666 }
667
668 return 0;
669 }
670
Reset(uint8_t kind)671 int CRemoteZ_Base::Reset(uint8_t kind)
672 {
673 int err = 0;
674 /*
675 * TODO: I don't believe the zwaves have a "kind" of reset
676 * ... is this needed here?
677 */
678 if (kind != 2) {
679 return LC_ERROR;
680 }
681
682 if ((err = Write(TYPE_REQUEST, COMMAND_Z_RESET))) {
683 debug("Failed to write to remote");
684 return LC_ERROR_WRITE;
685 }
686 uint8_t rsp[60];
687 unsigned int len;
688 uint8_t status;
689 if ((err = Read(status, len, rsp))) {
690 debug("Failed to read to remote");
691 return LC_ERROR_READ;
692 }
693
694 /*
695 * TODO: Either the remote is gone at this point, and reading
696 * will fail, or we need a "finalize action" command afterwards,
697 * in which case we should check the actual return status from
698 * the remote.
699 */
700 /*
701 if (time[1] != TYPE_RESPONSE || time[2] != COMMAND_Z_RESET) {
702 return LC_ERROR_INVALID_DATA_FROM_REMOTE;
703 }
704 */
705
706 return 0;
707 }
708
GetIdentity(TRemoteInfo & ri,THIDINFO & hid,lc_callback cb,void * cb_arg,uint32_t cb_stage)709 int CRemoteZ_Base::GetIdentity(TRemoteInfo &ri, THIDINFO &hid, lc_callback cb,
710 void *cb_arg, uint32_t cb_stage)
711 {
712 int err = 0;
713 if ((err = Write(TYPE_REQUEST, COMMAND_GET_SYSTEM_INFO))) {
714 debug("Failed to write to remote");
715 return LC_ERROR_WRITE;
716 }
717 uint8_t rsp[60];
718 unsigned int len;
719 uint8_t status;
720 if ((err = Read(status, len, rsp))) {
721 debug("Failed to read from remote");
722 return LC_ERROR_READ;
723 }
724
725 if (cb) {
726 cb(cb_stage, 0, 1, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
727 }
728
729 CRemoteZ_Base::TParamList pl;
730 ParseParams(len, rsp, pl);
731
732 ri.flash_mfg = 0x01;
733 ri.flash_id = 0x49;
734 ri.architecture = GetWord(pl.p[2]);
735 ri.fw_ver_major = GetWord(pl.p[3]);
736 ri.fw_ver_minor = GetWord(pl.p[4]);
737 ri.fw_type = *pl.p[5];
738 ri.skin = GetWord(pl.p[6]);
739 const unsigned int hw = GetWord(pl.p[7]);
740 ri.hw_ver_major = hw >> 8; // ???
741 ri.hw_ver_minor = (hw >> 4) & 0x0F; // ???
742 ri.hw_ver_micro = hw & 0x0F; // 900 has this and is non-zero
743 //ri.hw_ver_major = hw >> 4;
744 //ri.hw_ver_minor = hw & 0x0F;
745 ri.protocol = GetWord(pl.p[2]); // Seems to be the same as arch?
746
747 setup_ri_pointers(ri);
748
749 if (IsUSBNet()) {
750 hid.vid = GetWord(pl.p[0]);
751 hid.pid = GetWord(pl.p[1]);
752 hid.ver = 0;
753 hid.irl = 0;
754 hid.orl = 0;
755 hid.frl = 0;
756 hid.mfg = ri.model->mfg;
757 hid.prod = ri.model->model;
758 }
759
760 if ((err = Write(TYPE_REQUEST, COMMAND_GET_GUID))) {
761 debug("Failed to write to remote");
762 return LC_ERROR_WRITE;
763 }
764 if ((err = Read(status, len, rsp))) {
765 debug("Failed to read from remote");
766 return LC_ERROR_READ;
767 }
768
769 if (cb) {
770 cb(LC_CB_STAGE_GET_IDENTITY, 1, 2, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg,
771 NULL);
772 }
773
774 ParseParams(len, rsp, pl);
775
776 make_serial(pl.p[0], ri);
777
778 if (IsUSBNet()) {
779 // Get the User Config Region to find the config bytes used.
780 if ((err = ReadRegion(REGION_USER_CONFIG, ri.config_bytes_used, NULL,
781 NULL, NULL, 0))) {
782 return err;
783 }
784 } else {
785 ri.config_bytes_used = 0;
786 }
787 ri.max_config_size = 1;
788 ri.valid_config = 1;
789
790 if (!IsUSBNet()) {
791 return 0;
792 }
793
794 // Get region info - everything below is extra stuff that only the
795 // usbnet remotes seem to use.
796 uint8_t rr[] = { 1, 1, 1 }; // AddByteParam(1);
797 if ((err = Write(TYPE_REQUEST, COMMAND_GET_REGION_IDS, 3, rr))) {
798 debug("Failed to write to remote");
799 return LC_ERROR;
800 }
801 uint8_t rgn[64];
802 if ((err = Read(status, len, rgn))) {
803 debug("Failed to read from remote");
804 return LC_ERROR;
805 }
806 ParseParams(len, rgn, pl);
807 if (pl.count == 1) {
808 const unsigned int rc = *(pl.p[0]-1) & 0x3F;
809 ri.num_regions = rc + 1;
810 ri.region_ids = new uint8_t[ri.num_regions];
811 ri.region_versions = new char*[ri.num_regions];
812
813 // There seems to be an implied Region 0 that the Windows
814 // software requests but it is not listed in the Region IDs.
815 uint8_t rz[] = { 1, 1, 0 }; // 1 parameter, 1 byte, value = 0
816 if ((err = Write(TYPE_REQUEST, COMMAND_GET_REGION_VERSION, 3, rz))) {
817 debug("Failed to write to remote");
818 return LC_ERROR;
819 }
820 uint8_t rgz[64];
821 if ((err = Read(status, len, rgz))) {
822 debug("Failed to read from remote");
823 return LC_ERROR;
824 }
825 CRemoteZ_Base::TParamList rzp;
826 ParseParams(len, rgz, rzp);
827 ri.region_ids[0] = 0x00; // Store region 0.
828 ri.region_versions[0] = new char[4];
829 snprintf(ri.region_versions[0], 4, "%d.%d", *(rzp.p[0]+1), *rzp.p[0]);
830
831 for(unsigned int r = 0; r < rc; ++r) {
832 const uint8_t rn = *(pl.p[0]+r);
833 debug("Region %i", rn);
834 uint8_t rv[] = { 1, 1, rn }; // AddByteParam(rn);
835 if ((err = Write(TYPE_REQUEST, COMMAND_GET_REGION_VERSION, 3,
836 rv))) {
837 debug("Failed to write to remote");
838 return LC_ERROR;
839 }
840 uint8_t rgv[64];
841 if ((err = Read(status, len, rgv))) {
842 debug("Failed to read from remote");
843 return LC_ERROR;
844 }
845 CRemoteZ_Base::TParamList rp;
846 ParseParams(len, rgv, rp);
847 ri.region_ids[r+1] = rn;
848 ri.region_versions[r+1] = new char[4];
849 snprintf(ri.region_versions[r+1], 4, "%d.%d", *(rp.p[0]+1),
850 *rp.p[0]);
851 }
852 }
853
854 // Get HOMEID, NODEID, TID
855 if ((err = Write(TYPE_REQUEST, COMMAND_GET_HOME_ID))) {
856 debug("Failed to write to remote");
857 return LC_ERROR;
858 }
859 if ((err = Read(status, len, rsp))) {
860 debug("Failed to read from remote");
861 return LC_ERROR;
862 }
863 ParseParams(len, rsp, pl);
864 ri.home_id = (*pl.p[0] << 24) + (*(pl.p[0]+1) << 16) +
865 (*(pl.p[0]+2) << 8) + *(pl.p[0]+3);
866 if ((err = Write(TYPE_REQUEST, COMMAND_GET_NODE_ID))) {
867 debug("Failed to write to remote");
868 return LC_ERROR;
869 }
870 if ((err = Read(status, len, rsp))) {
871 debug("Failed to read from remote");
872 return LC_ERROR;
873 }
874 ParseParams(len, rsp, pl);
875 ri.node_id = *pl.p[0];
876 // Get the "TID" - not entirely sure what this is, but it appears to be
877 // contained in the INTERFACE_LIST.
878 if ((err = Write(TYPE_REQUEST, COMMAND_GET_INTERFACE_LIST))) {
879 debug("Failed to write to remote");
880 return LC_ERROR;
881 }
882 if ((err = Read(status, len, rsp))) {
883 debug("Failed to read from remote");
884 return LC_ERROR;
885 }
886 ParseParams(len, rsp, pl);
887 ri.tid = new char[21];
888 ri.tid[0] = '0';
889 ri.tid[1] = 'x';
890 // "TID" appears to be contained in the middle of the INTERFACE_LIST.
891 for (int i = 0; i < 9; i++)
892 snprintf(&ri.tid[(i*2)+2], 3, "%02X", *(pl.p[1]+i+2));
893 ri.tid[20] = '\0';
894
895 // Get the "XMLUserRFSetting" - the usbnet remote appears to have a web
896 // server running where the Windows driver grabs this data.
897 if ((err = GetXMLUserRFSetting(&ri.xml_user_rf_setting))) {
898 debug("Failed to read XML User RF Settings");
899 return LC_ERROR;
900 }
901
902 return 0;
903 }
904
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)905 int CRemoteZ_Base::ReadFlash(uint32_t addr, const uint32_t len, uint8_t *rd,
906 unsigned int protocol, bool verify, lc_callback cb,
907 void *cb_arg, uint32_t cb_stage)
908 {
909 uint32_t tmp;
910 return ReadRegion(addr, tmp, rd, cb, cb_arg, cb_stage);
911 }
912
913 /*
914 * When reading a config from the remote, we need to look for a sequence of
915 * four bytes to determine when to stop reading the config. The tricky part is
916 * that the sequence could be split across two packets. Thus, we need to
917 * search the last three bytes of the previous packet plus the current packet.
918 * This function searches for the "end of file" sequence in a set of two
919 * packets. If the sequence is found, returns the number of bytes in the
920 * second packet up to and including the sequence. Returns 0 if the sequence
921 * is not found. Parameters point to the data section of the packets. Note for
922 * the first packet, only the last 3 bytes are expected to be passed in.
923 */
FindEndSeq(uint8_t * pkt_1,uint8_t * pkt_2)924 int FindEndSeq(uint8_t *pkt_1, uint8_t *pkt_2)
925 {
926 uint8_t end_seq[4] = { 0x44, 0x4B, 0x44, 0x4B }; // end of file sequence
927 uint8_t tmp[57]; // 3 bytes from the 1st packet, 54 bytes from the 2nd
928 memcpy(&tmp, pkt_1, 3);
929 memcpy(&tmp[3], pkt_2, 54);
930 for (int i=0; i<54; i++) {
931 if (memcmp(&end_seq, &tmp[i], 4) == 0) {
932 return i + 1;
933 }
934 }
935 return 0;
936 }
937
ReadRegion(uint8_t region,uint32_t & rgn_len,uint8_t * rd,lc_callback cb,void * cb_arg,uint32_t cb_stage)938 int CRemoteZ_HID::ReadRegion(uint8_t region, uint32_t &rgn_len, uint8_t *rd,
939 lc_callback cb, void *cb_arg, uint32_t cb_stage)
940 {
941 int err = 0;
942 int cb_count = 0;
943 uint8_t rsp[60];
944 unsigned int rlen;
945 uint8_t status;
946
947 /* Start a TCP transfer */
948 if ((err = Write(TYPE_REQUEST, COMMAND_INITIATE_UPDATE_TCP_CHANNEL))) {
949 debug("Failed to write to remote");
950 return LC_ERROR_WRITE;
951 }
952
953 /* Make sure the remote is ready to start the TCP transfer */
954 if ((err = Read(status, rlen, rsp))) {
955 debug("Failed to read from remote");
956 return LC_ERROR_READ;
957 }
958
959 if (rsp[1] != TYPE_RESPONSE || rsp[2] !=
960 COMMAND_INITIATE_UPDATE_TCP_CHANNEL) {
961 return LC_ERROR;
962 }
963
964 /* Look for a SYN packet */
965 debug("Looking for syn");
966 if ((err = TCP_Read(status, rlen, rsp))) {
967 debug("Failed to read syn from remote");
968 return LC_ERROR_READ;
969 }
970
971 if (rsp[0] != TYPE_TCP_SYN) {
972 debug("Not a SYN packet!");
973 return LC_ERROR;
974 }
975
976 /* ACK it with a command to read a region */
977 debug("READ_REGION");
978 // 1 parameters, 1 byte, region to read.
979 uint8_t cmd[60] = { region };
980 if ((err = TCP_Write(TYPE_REQUEST, COMMAND_READ_REGION, 1, cmd))) {
981 debug("Failed to write to remote");
982 return LC_ERROR_WRITE;
983 }
984 if ((err = TCP_Read(status, rlen, rsp))) {
985 debug("Failed to read to remote");
986 return LC_ERROR_READ;
987 }
988 if (rsp[0] != TYPE_TCP_ACK || rsp[3] != TYPE_RESPONSE ||
989 rsp[4] != COMMAND_READ_REGION) {
990 debug("Incorrect response type from remote");
991 return LC_ERROR_INVALID_DATA_FROM_REMOTE;
992 }
993
994 rgn_len = 0;
995
996 debug("READ_REGION_DATA");
997 int data_read = 0;
998 uint8_t *rd_ptr = rd;
999 cmd[0] = region;
1000 int eof_found = 0;
1001 uint8_t prev_pkt_tail[3] = { 0x00, 0x00, 0x00 };
1002
1003 while (1) {
1004 if ((err = TCP_Write(TYPE_REQUEST, COMMAND_READ_REGION_DATA, 1, cmd))) {
1005 debug("Failed to write to remote");
1006 return LC_ERROR_WRITE;
1007 }
1008 if ((err = TCP_Read(status, rlen, rsp))) {
1009 debug("Failed to read to remote");
1010 return LC_ERROR_READ;
1011 }
1012 if (rsp[0] != TYPE_TCP_ACK || rsp[3] != TYPE_RESPONSE ||
1013 ((rsp[4] != COMMAND_READ_REGION_DATA) &&
1014 (rsp[4] != COMMAND_READ_REGION_DONE))) {
1015 debug("Incorrect response type from remote");
1016 return LC_ERROR_INVALID_DATA_FROM_REMOTE;
1017 }
1018 if (rsp[4] == COMMAND_READ_REGION_DONE) {
1019 break;
1020 }
1021 data_read += rlen;
1022
1023 if (!eof_found) {
1024 eof_found = FindEndSeq(prev_pkt_tail, &rsp[5]);
1025 if (eof_found) {
1026 rlen = eof_found;
1027 }
1028 rgn_len += rlen;
1029 memcpy(&prev_pkt_tail, &rsp[56], 3);
1030
1031 if (rd) {
1032 memcpy(rd_ptr, &rsp[5], rlen);
1033 rd_ptr += rlen;
1034 }
1035 }
1036
1037 debug("DATA %d, read %d bytes, %d bytes total", cb_count, rlen,
1038 data_read);
1039
1040 if (cb) {
1041 cb(cb_stage, cb_count++, data_read, data_read+1,
1042 LC_CB_COUNTER_TYPE_BYTES, cb_arg, NULL);
1043 }
1044 }
1045
1046 debug("FIN-ACK");
1047 if ((err = TCP_Ack(false, true))) {
1048 debug("Failed to send fin-ack");
1049 return LC_ERROR_WRITE;
1050 }
1051
1052 if ((err = TCP_Read(status, rlen, rsp))) {
1053 debug("Failed to read from remote");
1054 return LC_ERROR_READ;
1055 }
1056
1057 /* Make sure we got an ack */
1058 if (rsp[0] != (TYPE_TCP_ACK | TYPE_TCP_FIN)) {
1059 debug("Failed to read finish-update ack");
1060 return LC_ERROR;
1061 }
1062
1063 if ((err = TCP_Ack(true, false))) {
1064 debug("Failed to ack the ack of our fin-ack");
1065 return LC_ERROR_WRITE;
1066 }
1067
1068 /* Return TCP state to initial conditions */
1069 SYN_ACKED = false;
1070
1071 if (cb) {
1072 cb(cb_stage, cb_count++, data_read, data_read, LC_CB_COUNTER_TYPE_BYTES,
1073 cb_arg, NULL);
1074 }
1075
1076 return 0;
1077 }
1078
InvalidateFlash(lc_callback cb,void * cb_arg,uint32_t cb_stage)1079 int CRemoteZ_Base::InvalidateFlash(lc_callback cb, void *cb_arg,
1080 uint32_t cb_stage)
1081 {
1082 return 0;
1083 }
1084
EraseFlash(uint32_t addr,uint32_t len,const TRemoteInfo & ri,lc_callback cb,void * cb_arg,uint32_t cb_stage)1085 int CRemoteZ_Base::EraseFlash(uint32_t addr, uint32_t len,
1086 const TRemoteInfo &ri, lc_callback cb,
1087 void *cb_arg, uint32_t cb_stage)
1088 {
1089 return 0;
1090 }
1091
WriteFlash(uint32_t addr,const uint32_t len,const uint8_t * wr,unsigned int protocol,lc_callback cb,void * arg,uint32_t cb_stage)1092 int CRemoteZ_Base::WriteFlash(uint32_t addr, const uint32_t len,
1093 const uint8_t *wr, unsigned int protocol,
1094 lc_callback cb, void *arg, uint32_t cb_stage)
1095 {
1096 return 0;
1097 }
1098
WriteRam(uint32_t addr,const uint32_t len,uint8_t * wr)1099 int CRemoteZ_Base::WriteRam(uint32_t addr, const uint32_t len, uint8_t *wr)
1100 {
1101 return 0;
1102 }
1103
ReadRam(uint32_t addr,const uint32_t len,uint8_t * rd)1104 int CRemoteZ_Base::ReadRam(uint32_t addr, const uint32_t len, uint8_t *rd)
1105 {
1106 return 0;
1107 }
1108
PrepFirmware(const TRemoteInfo & ri,lc_callback cb,void * cb_arg,uint32_t cb_stage)1109 int CRemoteZ_Base::PrepFirmware(const TRemoteInfo &ri, lc_callback cb,
1110 void *cb_arg, uint32_t cb_stage)
1111 {
1112 return 0;
1113 }
1114
FinishFirmware(const TRemoteInfo & ri,lc_callback cb,void * cb_arg,uint32_t cb_stage)1115 int CRemoteZ_Base::FinishFirmware(const TRemoteInfo &ri, lc_callback cb,
1116 void *cb_arg, uint32_t cb_stage)
1117 {
1118 return 0;
1119 }
1120
PrepConfig(const TRemoteInfo & ri,lc_callback cb,void * cb_arg,uint32_t cb_stage)1121 int CRemoteZ_Base::PrepConfig(const TRemoteInfo &ri, lc_callback cb,
1122 void *cb_arg, uint32_t cb_stage)
1123 {
1124 return 0;
1125 }
1126
FinishConfig(const TRemoteInfo & ri,lc_callback cb,void * cb_arg,uint32_t cb_stage)1127 int CRemoteZ_Base::FinishConfig(const TRemoteInfo &ri, lc_callback cb,
1128 void *cb_arg, uint32_t cb_stage)
1129 {
1130 return 0;
1131 }
1132
GetTime(const TRemoteInfo & ri,THarmonyTime & ht)1133 int CRemoteZ_Base::GetTime(const TRemoteInfo &ri, THarmonyTime &ht)
1134 {
1135 int err = 0;
1136 if ((err = Write(TYPE_REQUEST, COMMAND_GET_CURRENT_TIME))) {
1137 debug("Failed to write to remote");
1138 return LC_ERROR_WRITE;
1139 }
1140 uint8_t time[60];
1141 unsigned int len;
1142 uint8_t status;
1143 if ((err = Read(status, len, time))) {
1144 debug("Failed to read to remote");
1145 return LC_ERROR_READ;
1146 }
1147
1148 if (time[1] != TYPE_RESPONSE || time[2] != COMMAND_GET_CURRENT_TIME) {
1149 debug("Incorrect response type from Get Time");
1150 return LC_ERROR_INVALID_DATA_FROM_REMOTE;
1151 }
1152
1153 CRemoteZ_Base::TParamList pl;
1154 ParseParams(len, time, pl);
1155
1156 ht.year = GetWord(pl.p[0]);
1157 ht.month = *pl.p[1];
1158 ht.day = *pl.p[2];
1159 ht.hour = *pl.p[3];
1160 ht.minute = *pl.p[4];
1161 ht.second = *pl.p[5];
1162 ht.dow = *pl.p[6]&7;
1163 ht.utc_offset = static_cast<int16_t>(GetWord(pl.p[7]));
1164 if (pl.count > 11) {
1165 ht.timezone = reinterpret_cast<char*>(pl.p[11]);
1166 } else {
1167 ht.timezone = "";
1168 }
1169
1170 return 0;
1171 }
1172
SetTime(const TRemoteInfo & ri,const THarmonyTime & ht,lc_callback cb,void * cb_arg,uint32_t cb_stage)1173 int CRemoteZ_Base::SetTime(const TRemoteInfo &ri, const THarmonyTime &ht,
1174 lc_callback cb, void *cb_arg, uint32_t cb_stage)
1175 {
1176 int err = 0;
1177
1178 uint8_t tsv[16] = { static_cast<uint8_t>(ht.year), 0, // 2 bytes
1179 static_cast<uint8_t>(ht.month),
1180 static_cast<uint8_t>(ht.day),
1181 static_cast<uint8_t>(ht.hour),
1182 static_cast<uint8_t>(ht.minute),
1183 static_cast<uint8_t>(ht.second),
1184 static_cast<uint8_t>(ht.dow),
1185 // utcOffset
1186 static_cast<uint8_t>(ht.utc_offset), 0, // 2 bytes
1187 // 0s
1188 0, 0, // 2 bytes
1189 0, 0, // 2 bytes
1190 0, 0, // 2 bytes
1191 // ht.timezone.c_str()
1192 };
1193
1194 if ((err = Write(TYPE_REQUEST, COMMAND_UPDATE_TIME, 16, tsv))) {
1195 debug("Failed to write to remote");
1196 return LC_ERROR_WRITE;
1197 }
1198
1199 uint8_t rsp[60];
1200 unsigned int len;
1201 uint8_t status;
1202 if ((err = Read(status, len, rsp))) {
1203 debug("failed to read from remote");
1204 return LC_ERROR_READ;
1205 }
1206
1207 if (rsp[1] != TYPE_RESPONSE || rsp[2] != COMMAND_UPDATE_TIME) {
1208 return LC_ERROR;
1209 }
1210
1211 return 0;
1212 }
1213
TCPSendAndCheck(uint8_t cmd,uint32_t len,uint8_t * data,bool ackonly)1214 int CRemoteZ_HID::TCPSendAndCheck(uint8_t cmd, uint32_t len, uint8_t *data,
1215 bool ackonly)
1216 {
1217 int err = 0;
1218 uint8_t status;
1219 unsigned int rlen;
1220 uint8_t rsp[60];
1221
1222 if ((err = TCP_Write(TYPE_REQUEST, cmd, len, data))) {
1223 debug("Failed to send request %02X", cmd);
1224 return LC_ERROR_WRITE;
1225 }
1226
1227 if ((err = TCP_Read(status, rlen, rsp))) {
1228 debug("Failed to read from remote");
1229 return LC_ERROR_READ;
1230 }
1231
1232 /* make sure it was the response we expected */
1233 if (rsp[0] != TYPE_TCP_ACK) {
1234 debug("Packet wasn't an ACK!");
1235 return LC_ERROR;
1236 }
1237 if (!ackonly) {
1238 if (rsp[3] != TYPE_RESPONSE) {
1239 debug("Packet didn't have response bit!");
1240 return LC_ERROR;
1241 }
1242 if (rsp[4] != cmd) {
1243 debug("The cmd bit didn't match our request packet");
1244 return LC_ERROR;
1245 }
1246 }
1247
1248 return 0;
1249 }
1250
UpdateConfig(const uint32_t len,const uint8_t * wr,lc_callback cb,void * cb_arg,uint32_t cb_stage,uint32_t xml_size,uint8_t * xml)1251 int CRemoteZ_HID::UpdateConfig(const uint32_t len, const uint8_t *wr,
1252 lc_callback cb, void *cb_arg, uint32_t cb_stage,
1253 uint32_t xml_size, uint8_t *xml)
1254 {
1255 int err = 0;
1256 int cb_count = 0;
1257
1258 cb(LC_CB_STAGE_INITIALIZE_UPDATE, cb_count++, 0, 4,
1259 LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
1260 /* Start a TCP transfer */
1261 if ((err = Write(TYPE_REQUEST, COMMAND_INITIATE_UPDATE_TCP_CHANNEL))) {
1262 debug("Failed to write to remote");
1263 return LC_ERROR_WRITE;
1264 }
1265
1266 uint8_t rsp[60];
1267 unsigned int rlen;
1268 uint8_t status;
1269
1270 /* Make sure the remote is ready to start the TCP transfer */
1271 if ((err = Read(status, rlen, rsp))) {
1272 debug("Failed to read from remote");
1273 return LC_ERROR_READ;
1274 }
1275
1276 if (rsp[1] != TYPE_RESPONSE || rsp[2] !=
1277 COMMAND_INITIATE_UPDATE_TCP_CHANNEL) {
1278 return LC_ERROR;
1279 }
1280 cb(LC_CB_STAGE_INITIALIZE_UPDATE, cb_count++, 1, 4,
1281 LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
1282
1283 /* Look for a SYN packet */
1284 debug("Looking for syn");
1285 if ((err = TCP_Read(status, rlen, rsp))) {
1286 debug("Failed to read syn from remote");
1287 return LC_ERROR_READ;
1288 }
1289
1290 if (rsp[0] != TYPE_TCP_SYN) {
1291 debug("Not a SYN packet!");
1292 return LC_ERROR;
1293 }
1294 cb(LC_CB_STAGE_INITIALIZE_UPDATE, cb_count++, 2, 4,
1295 LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
1296
1297 /* ACK it with a command to start an update */
1298 debug("START_UPDATE");
1299 uint8_t cmd[60] = { 0x00, 0x04 };
1300 if ((err = TCP_Write(TYPE_REQUEST, COMMAND_START_UPDATE, 2, cmd))) {
1301 debug("Failed to write start-update to remote");
1302 return LC_ERROR_WRITE;
1303 }
1304
1305 if ((err = TCP_Read(status, rlen, rsp))) {
1306 debug("Failed to read from remote");
1307 return LC_ERROR_READ;
1308 }
1309
1310 /* make sure ot says 'start update response' */
1311 if (rsp[0] != TYPE_TCP_ACK || rsp[3] != TYPE_RESPONSE ||
1312 rsp[4] != COMMAND_START_UPDATE) {
1313 debug("Not expected ack");
1314 return LC_ERROR;
1315 }
1316 cb(LC_CB_STAGE_INITIALIZE_UPDATE, cb_count++, 3, 4,
1317 LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
1318
1319 /* write update-header */
1320 debug("UPDATE_HEADER");
1321 uint32_t nlen = len;
1322 unsigned char *size_ptr = (unsigned char *)&nlen;
1323 for (int i = 0; i < 4; i++) {
1324 cmd[i] = size_ptr[i];
1325 }
1326 cmd[4] = 0x04;
1327 if ((err = TCPSendAndCheck(COMMAND_WRITE_UPDATE_HEADER, 5, cmd))) {
1328 return err;
1329 }
1330 cb(LC_CB_STAGE_INITIALIZE_UPDATE, cb_count++, 4, 4,
1331 LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
1332 cb_count = 0;
1333
1334 /* write data */
1335 debug("UPDATE_DATA");
1336 int pkt_len;
1337 int tlen = len;
1338 uint8_t *wr_ptr = const_cast<uint8_t*>(wr);
1339 while (tlen) {
1340 pkt_len = 58;
1341 if (tlen < pkt_len) {
1342 pkt_len = tlen;
1343 }
1344 tlen -= pkt_len;
1345
1346 debug("DATA %d, sending %d bytes, %d bytes left", cb_count,
1347 pkt_len, tlen);
1348
1349 if ((err = TCPSendAndCheck(COMMAND_WRITE_UPDATE_DATA, pkt_len,
1350 wr_ptr, true))) {
1351 return err;
1352 }
1353 wr_ptr += pkt_len;
1354
1355 if (cb) {
1356 cb(LC_CB_STAGE_WRITE_CONFIG, cb_count++, (int)(wr_ptr - wr), len,
1357 LC_CB_COUNTER_TYPE_BYTES, cb_arg, NULL);
1358 }
1359 }
1360
1361 /* write update-done */
1362 cb_count = 0;
1363 cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 0, 6, LC_CB_COUNTER_TYPE_STEPS,
1364 cb_arg, NULL);
1365 debug("UPDATE_DATA_DONE");
1366 if ((err = TCPSendAndCheck(COMMAND_WRITE_UPDATE_DATA_DONE))) {
1367 return err;
1368 }
1369 cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 1, 6, LC_CB_COUNTER_TYPE_STEPS,
1370 cb_arg, NULL);
1371
1372 /* Funky ACK exchange - part 1 */
1373 debug("FUNKY-ACK");
1374 if ((err = TCP_Ack(false, false))) {
1375 debug("Failed to send funky-ack");
1376 return LC_ERROR_WRITE;
1377 }
1378
1379 if ((err = TCP_Read(status, rlen, rsp))) {
1380 debug("Failed to read from remote");
1381 return LC_ERROR_READ;
1382 }
1383
1384 /* Funky ACK exchange - part 2 */
1385 debug("FUNKY-ACK");
1386 if ((err = TCP_Ack(false, false))) {
1387 debug("Failed to send funky-ack");
1388 return LC_ERROR_WRITE;
1389 }
1390
1391 if ((err = TCP_Read(status, rlen, rsp))) {
1392 debug("Failed to read from remote");
1393 return LC_ERROR_READ;
1394 }
1395 cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 2, 6, LC_CB_COUNTER_TYPE_STEPS,
1396 cb_arg, NULL);
1397
1398 /* send get-cheksum */
1399 debug("GET_CHECKSUM");
1400 cmd[0] = 0xFF;
1401 cmd[1] = 0xFF;
1402 cmd[2] = 0x04;
1403 if ((err = TCPSendAndCheck(COMMAND_GET_UPDATE_CHECKSUM, 3, cmd))) {
1404 return err;
1405 }
1406 cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 3, 6, LC_CB_COUNTER_TYPE_STEPS,
1407 cb_arg, NULL);
1408
1409 /* send finish-update */
1410 debug("FINISH_UPDATE");
1411 cmd[0] = 0x01;
1412 cmd[1] = 0x04;
1413 /*
1414 * OK, this is a bit weird. We will eventually get a response to the
1415 * FINISH_UPDATE, but first we get an "empty" ack, do another funky-ack
1416 * exchange, and then we'll get the real response to this FINISH_UPDATE.
1417 */
1418 if ((err = TCPSendAndCheck(COMMAND_FINISH_UPDATE, 2, cmd, true))) {
1419 return err;
1420 }
1421 cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 4, 6, LC_CB_COUNTER_TYPE_STEPS,
1422 cb_arg, NULL);
1423
1424 /* Funky ACK exchange */
1425 debug("FUNKY-ACK");
1426 if ((err = TCP_Ack(false, false))) {
1427 debug("Failed to send funky-ack");
1428 return LC_ERROR_WRITE;
1429 }
1430
1431 /* Read their empty ack */
1432 if ((err = TCP_Read(status, rlen, rsp))) {
1433 debug("Failed to read from remote");
1434 return LC_ERROR_READ;
1435 }
1436 if (rsp[0] != TYPE_TCP_ACK) {
1437 debug("Failed to read ack");
1438 return LC_ERROR;
1439 }
1440
1441 /* And then we should have the response we want. */
1442 if ((err = TCP_Read(status, rlen, rsp))) {
1443 debug("Failed to read from remote");
1444 return LC_ERROR_READ;
1445 }
1446
1447 /* make sure we got an ack */
1448 if (rsp[0] != TYPE_TCP_ACK || rsp[3] != TYPE_RESPONSE ||
1449 rsp[4] != COMMAND_FINISH_UPDATE) {
1450 debug("Failed to read finish-update ack");
1451 return LC_ERROR;
1452 }
1453 cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 5, 6, LC_CB_COUNTER_TYPE_STEPS,
1454 cb_arg, NULL);
1455
1456 /* FIN-ACK */
1457 debug("FIN-ACK");
1458 if ((err = TCP_Ack(false, true))) {
1459 debug("Failed to send fin-ack");
1460 return LC_ERROR_WRITE;
1461 }
1462
1463 if ((err = TCP_Read(status, rlen, rsp))) {
1464 debug("Failed to read from remote");
1465 return LC_ERROR_READ;
1466 }
1467
1468 /* Make sure we got an ack */
1469 if (rsp[0] != (TYPE_TCP_ACK | TYPE_TCP_FIN)) {
1470 debug("Failed to read finish-update ack");
1471 return LC_ERROR;
1472 }
1473
1474 if ((err = TCP_Ack(true, false))) {
1475 debug("Failed to ack the ack of our fin-ack");
1476 return LC_ERROR_WRITE;
1477 }
1478 cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 6, 6, LC_CB_COUNTER_TYPE_STEPS,
1479 cb_arg, NULL);
1480
1481 /*
1482 * Official traces seem to show a final ack to the above ack, but for us
1483 * it never comes... so we don't bother trying to read it.
1484 */
1485
1486 /* Return TCP state to initial conditions */
1487 SYN_ACKED = false;
1488
1489 return 0;
1490 }
1491
LearnIR(uint32_t * freq,uint32_t ** ir_signal,uint32_t * ir_signal_length,lc_callback cb,void * cb_arg,uint32_t cb_stage)1492 int CRemoteZ_HID::LearnIR(uint32_t *freq, uint32_t **ir_signal,
1493 uint32_t *ir_signal_length, lc_callback cb,
1494 void *cb_arg, uint32_t cb_stage)
1495 {
1496 return LC_ERROR_UNSUPP;
1497 }
1498
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)1499 int CRemoteZ_Base::ReadFile(const char *filename, uint8_t *rd,
1500 const uint32_t rdlen, uint32_t *data_read,
1501 uint8_t start_seq, lc_callback cb, void *cb_arg,
1502 uint32_t cb_stage)
1503 {
1504 return LC_ERROR_UNSUPP;
1505 }
1506
WriteFile(const char * filename,uint8_t * wr,const uint32_t wrlen)1507 int CRemoteZ_Base::WriteFile(const char *filename, uint8_t *wr,
1508 const uint32_t wrlen)
1509 {
1510 return LC_ERROR_UNSUPP;
1511 }
1512