1 /*
2 DeLorme PN-20/40 USB "DeLBin" protocol
3
4 Copyright (C) 2009 Paul Cornett, pc-gpsb at bullseye.com
5 Copyright (C) 2005-2014 Robert Lipe, robertlipe+source@gpsbabel.orgg
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21 */
22
23 #include "defs.h"
24 #include "src/core/xmltag.h"
25 #include <assert.h>
26 #include <stdio.h> /* for atoi, sprintf */
27 #include <stdlib.h> // atoi
28
29 #define MYNAME "delbin"
30 static short_handle mkshort_handle;
31
32 /*
33 Device documentation:
34 "DeLorme Binary GPS Format", delbin_user_interface_format_176.pdf
35 obtained here: http://forum.delorme.com/viewtopic.php?t=13846
36
37 Notes:
38 Initial development was done with a PN-40, firmware 2.4.123299. The test
39 device was upgraded to firmware 2.5.165506 during development.
40
41 The "data size" in the message header includes the 4 trailer bytes, so it
42 is really the size of the whole message minus the header.
43
44 Messages do not always start at the beginning of a packet. Every once in a
45 while, the start of the next message directly follows the end of the previous
46 one, in the same packet.
47
48 The time before an unacknowledged message will be retransmitted by the
49 device is on the order of 2 to 4 seconds.
50
51 Retrieving all tracks at once (using code 0 in message 0xb031) does not
52 seem to work, it hangs after the first track, maybe waiting for some
53 undocumented response message.
54
55 Character encoding is not documented, appears to be 8859-1.
56
57 The undocumented messages 0xaa01, 0xb015, 0xb016 and the use of the
58 "reserved" byte in message 0xb012 were discovered by examining the data
59 transferred between the device and DeLorme Topo 8.0. They may have been
60 added in the PN-40 2.5 firmware.
61 */
62
63 //-----------------------------------------------------------------------------
64 // interface to platform-specific device I/O
65 typedef struct {
66 void (*init)(const QString& name);
67 void (*deinit)(void);
68 unsigned(*packet_read)(void*);
69 unsigned(*packet_write)(const void*, unsigned);
70 } delbin_os_ops_t;
71
72 // really static, only extern so it can be forward declared
73 extern delbin_os_ops_t delbin_os_ops;
74
75 static unsigned delbin_os_packet_size;
76 //-----------------------------------------------------------------------------
77
78 // number of times to attempt a transfer before giving up
79 #define ATTEMPT_MAX 2
80 // seconds to wait for expected message (actual time will be somewhat
81 // indeterminate, but at least READ_TIMEOUT - 1)
82 #define READ_TIMEOUT 6
83
84 // debug output: low, medium, high, higher
85 #define DBGLVL_L 1
86 #define DBGLVL_M 2
87 #define DBGLVL_H 3
88 #define DBGLVL_H2 4
89
90 // Multiple unit support.
91 #define DELBIN_MAX_UNITS 32
92 static struct {
93 unsigned int unit_number;
94 const char* unit_serial_number;
95 const char* unit_name;
96 } delbin_unit_info[DELBIN_MAX_UNITS];
97 static int n_delbin_units;
98
99 #define UNKNOWN_ELEV -2000000
100
101 #define sizeofarray(x) (sizeof(x) / sizeof(x[0]))
102
103 static char* opt_getposn = NULL;
104 static char* opt_logs = NULL;
105 static char* opt_long_notes = NULL;
106 static char* opt_nuke_wpt = NULL;
107 static char* opt_nuke_trk = NULL;
108 static char* opt_nuke_rte = NULL;
109 /* If true, Order hint to match Cache Register and Topo 7 */
110 static char* opt_hint_at_end = NULL;
111 static char* opt_gcsym = NULL;
112
113
114 static arglist_t delbin_args[] = {
115 {
116 "get_posn", &opt_getposn, "Return current position as a waypoint",
117 NULL, ARGTYPE_BOOL, ARG_NOMINMAX
118 },
119 {
120 "logs", &opt_logs, "Include groundspeak logs when writing",
121 NULL, ARGTYPE_BOOL, ARG_NOMINMAX
122 },
123 {
124 "long_notes", &opt_long_notes, "Use long waypoint notes regardless of PN version",
125 NULL, ARGTYPE_BOOL, ARG_NOMINMAX
126 },
127 {
128 "nukewpt", &opt_nuke_wpt, "Delete all waypoints before sending", NULL, ARGTYPE_BOOL,
129 ARG_NOMINMAX
130 },
131 {
132 "nuketrk", &opt_nuke_trk, "Delete all tracks before sending", NULL, ARGTYPE_BOOL,
133 ARG_NOMINMAX
134 },
135 {
136 "nukerte", &opt_nuke_rte, "Delete all routes before sending", NULL, ARGTYPE_BOOL,
137 ARG_NOMINMAX
138 },
139 {"hint_at_end", &opt_hint_at_end, "If true, geocache hint at end of text", NULL, ARGTYPE_BOOL, ARG_NOMINMAX },
140 {"gcsym", &opt_gcsym, "If set to 0, prefer user-provided symbols over Groundspeaks ones for geocaches", "1", ARGTYPE_BOOL, ARG_NOMINMAX },
141 ARG_TERMINATOR
142 };
143
144 // Whether device understands message 0xb016
145 static int use_extended_notes;
146
147 // Device capabilities
148 static unsigned device_max_waypoint;
149
150 static const char* waypoint_symbol(unsigned index);
151 static unsigned waypoint_symbol_index(const char* name);
152 static int track_color(unsigned index);
153 static unsigned track_color_index(int bgr);
154
155 static unsigned waypoint_i;
156 static unsigned waypoint_n;
157 static Waypoint** wp_array;
158
159 //-----------------------------------------------------------------------------
160 // Message ids and sizes. Only the needed ones are here.
161 // Note that "in" and "out" ids are named as in the device documentation,
162 // so "in" means to the device, "out" means from.
163 #define MSG_ACK 0xaa00
164 #define MSG_BREAK 0xaa02
165 #define MSG_BREAK_SIZE 33
166 #define MSG_CAPABILITIES 0xb001
167 #define MSG_DELETE 0xb005
168 #define MSG_DELETE_SIZE 67
169 #define MSG_ERROR 0xa003
170 #define MSG_NAVIGATION 0xa010
171 #define MSG_REQUEST_ROUTES 0xb051
172 #define MSG_REQUEST_ROUTES_SIZE 65
173 #define MSG_REQUEST_TRACKS 0xb031
174 #define MSG_REQUEST_TRACKS_SIZE 33
175 #define MSG_REQUEST_WAYPOINTS 0xb012
176 #define MSG_REQUEST_WAYPOINTS_SIZE 15
177 #define MSG_ROUTE_COUNT 0xb050
178 #define MSG_ROUTE_HEADER_IN 0xb055
179 #define MSG_ROUTE_HEADER_OUT 0xb052
180 #define MSG_ROUTE_POINT_IN 0xb056
181 #define MSG_ROUTE_POINT_OUT 0xb053
182 #define MSG_ROUTE_SHAPE_IN 0xb057
183 #define MSG_ROUTE_SHAPE_OUT 0xb054
184 #define MSG_SATELLITE_INFO 0xa020
185 #define MSG_TRACK_COUNT 0xb030
186 #define MSG_TRACK_HEADER_IN 0xb035
187 #define MSG_TRACK_HEADER_OUT 0xb032
188 #define MSG_TRACK_POINT_IN 0xb036
189 #define MSG_TRACK_POINT_OUT 0xb033
190 #define MSG_TRANSFER_COMPLETE 0xaa04
191 #define MSG_VERSION 0xa001
192 #define MSG_WAYPOINT_COUNT 0xb010
193 #define MSG_WAYPOINT_IN 0xb014
194 #define MSG_WAYPOINT_OUT 0xb013
195 // Undocumented:
196 // This one looks like MSG_ACK, except it also has a string in it that says
197 // something like "device is busy". The expected MSG_ACK usually immediately
198 // follows it, so the point of this one is unclear.
199 #define MSG_NACK 0xaa01
200 // Long waypoint notes
201 #define MSG_WAYPOINT_NOTE_IN 0xb016
202 #define MSG_WAYPOINT_NOTE_OUT 0xb015
203
204 //-----------------------------------------------------------------------------
205 // Message structures
206
207 // Input Delete Message
208 // Message ID: 0xB005
209 typedef enum {
210 nuke_type_wpt = 0,
211 nuke_type_trk = 1,
212 nuke_type_rte = 2,
213 // int nuke_map = 3;
214 } nuke_type;
215
216 typedef enum {
217 nuke_mode_all = 0,
218 nuke_mode_single = 1
219 } nuke_mode;
220
221 typedef enum {
222 nuke_dest_internal = 0,
223 nuke_dest_sd = 1
224 } nuke_dest;
225
226 typedef struct {
227 uint8_t type;
228 uint8_t mode;
229 uint8_t location;
230 char object_name[64];
231 } msg_delete_t;
232
233 // Output Waypoint Message
234 // Message ID: 0xB013
235 // Input Waypoint Message
236 // Message ID: 0xB014
237 typedef struct {
238 uint8_t total[4]; // U32
239 uint8_t index[4]; // U32
240 uint8_t year;
241 uint8_t month;
242 uint8_t day;
243 uint8_t hour;
244 uint8_t minute;
245 uint8_t second;
246 uint8_t latitude[4]; // S32 rad * 100000000
247 uint8_t longitude[4]; // S32 rad * 100000000
248 uint8_t elevation[4]; // F32 meters
249 uint8_t color;
250 uint8_t symbol;
251 uint8_t name_size;
252 char name[1];
253 // note_size[2] U16
254 // note[note_size]
255 } msg_waypoint_t;
256
257 // undocumented, seen with PN-40 2.5 firmware
258 // output waypoint note
259 // Message ID: 0xB015
260 // input waypoint note
261 // Message ID: 0xB016
262 typedef struct {
263 uint8_t index[2];
264 uint8_t total[2];
265 uint8_t name_size;
266 char name[1];
267 // note_size[2]
268 // note[note_size]
269 } msg_waypoint_note_t;
270
271 // Output Track Point Message
272 // Message ID: 0xB033
273 // Input Track Point Message
274 // Message ID: 0xB036
275 typedef struct {
276 uint8_t total[4]; // U32
277 uint8_t index[4]; // U32
278 uint8_t number;
279 struct {
280 uint8_t year;
281 uint8_t month;
282 uint8_t day;
283 uint8_t hour;
284 uint8_t minute;
285 uint8_t second;
286 uint8_t latitude[4]; // S32 rad * 100000000
287 uint8_t longitude[4]; // S32 rad * 100000000
288 uint8_t elevation[4]; // F32 meters
289 uint8_t speed[2]; // U16 km/h * 10
290 uint8_t heading[2]; // U16 deg * 100
291 uint8_t status;
292 } point[1];
293 } msg_track_point_t;
294
295 // Output Track Header (Name) Message
296 // Message ID: 0xB032
297 typedef struct {
298 uint8_t total_tracks[2]; // U16
299 uint8_t number[2]; // U16
300 char name[32];
301 uint8_t total_points[4]; // U32
302 uint8_t year;
303 uint8_t month;
304 uint8_t day;
305 uint8_t hour;
306 uint8_t minute;
307 uint8_t second;
308 uint8_t color[2]; // U16
309 uint8_t distance[4]; // U32 m
310 uint8_t duration[4]; // U32 sec
311 uint8_t comment_size[2]; // U16
312 char comment[1];
313 } msg_track_header_t;
314
315 // Input Upload Track Header Message
316 // Message ID: 0xB035
317 typedef struct {
318 char name[32];
319 uint8_t total_points[4]; // U32
320 uint8_t year;
321 uint8_t month;
322 uint8_t day;
323 uint8_t hour;
324 uint8_t minute;
325 uint8_t second;
326 uint8_t color[2]; // U16
327 uint8_t comment_size[2]; // U16
328 char comment[1];
329 } msg_track_header_in_t;
330
331 // Output Route Shape Message
332 // Message ID: 0xB054
333 typedef struct {
334 uint8_t total[4]; // U32
335 uint8_t index[4]; // U32
336 uint8_t number;
337 uint8_t reserved;
338 struct {
339 uint8_t latitude[4]; // S32 rad * 100000000
340 uint8_t longitude[4]; // S32 rad * 100000000
341 } point[1];
342 } msg_route_shape_t;
343
344 // Output Route Point Message
345 // Message ID: 0xB053
346 // Input Route Itin Point Message
347 // Message ID: 0xB056
348 typedef struct {
349 uint8_t total[4]; // U32
350 uint8_t index[4]; // U32
351 char name[32];
352 uint8_t latitude[4]; // S32 rad * 100000000
353 uint8_t longitude[4]; // S32 rad * 100000000
354 uint8_t time_from_start[4]; // U32 sec
355 uint8_t distance_from_start[4]; // F32 km
356 uint8_t bearing_in[2]; // U16 deg * 100
357 uint8_t bearing_out[2]; // U16 deg * 100
358 uint8_t bearing_next[2]; // U16 deg * 100
359 uint8_t itinerary_type;
360 uint8_t turn_type;
361 uint8_t road_class[2]; // U16
362 uint8_t feature_code[4]; // U32
363 uint8_t exit_label_size;
364 char exit_label[1];
365 // comment_size U8
366 // comment[comment_size]
367 // shape_pt_count U32
368 } msg_route_point_t;
369
370 // Output Route Header (Name) Message
371 // Message ID: 0xB052
372 typedef struct {
373 uint8_t total[2]; // U16
374 uint8_t index[2]; // U16
375 char name[64];
376 uint8_t type;
377 uint8_t total_route_point[4]; // U32
378 uint8_t total_shape_point[4]; // U32
379 } msg_route_header_t;
380
381 // Input Upload Route Header Message
382 // Message ID: 0xB055
383 typedef struct {
384 char name[64];
385 uint8_t type;
386 uint8_t total_route_point[4]; // U32
387 uint8_t total_shape_point[4]; // U32
388 } msg_route_header_in_t;
389
390 // Output Navigation Message
391 // Message ID: 0xA010
392 typedef struct {
393 uint8_t gps_week[2]; // U16
394 uint8_t time_of_week[8]; // D64 sec
395 uint8_t year[2]; // U16
396 uint8_t month;
397 uint8_t day;
398 uint8_t hour;
399 uint8_t minute;
400 uint8_t second;
401 uint8_t satellites;
402 uint8_t latitude[8]; // D64 deg
403 uint8_t longitude[8]; // D64 deg
404 uint8_t elevation[8]; // D64 meters
405 uint8_t geoid_offset[2]; // S16 meters * 10
406 uint8_t speed[4]; // F32 km/h
407 uint8_t heading[2]; // U16 deg * 100
408 uint8_t magnetic_variation[2]; // S16 deg * 100
409 uint8_t fix_status;
410 } msg_navigation_t;
411
412 // Output Satellite Info Message
413 // Message ID: 0xA020
414 typedef struct {
415 uint8_t gps_week[2]; // U16
416 uint8_t time_of_week[8]; // D64 sec
417 uint8_t hdop[2]; // U16
418 uint8_t vdop[2]; // U16
419 uint8_t pdop[2]; // U16
420 uint8_t number;
421 struct {
422 uint8_t prn;
423 uint8_t azimuth[2]; // S16 deg? * 100
424 uint8_t elevation[2]; // S16 deg? * 100
425 uint8_t Cn0[2]; // U16 snr * 100
426 uint8_t status;
427 } sat[1];
428 } msg_satellite_t;
429
430 // Output Version Message
431 // Message ID: 0xA001
432 typedef struct {
433 uint8_t firmware_version[4];
434 char company[32];
435 char product[32];
436 char firmware[32];
437 char gps_firmware[48];
438 char serial[16];
439 char extra[16];
440 } msg_version_t;
441
442 // Output Device Capabilities Message
443 // Message ID: 0xB001
444 typedef struct {
445 uint8_t max_waypoints[4]; // U32
446 uint8_t max_tracks[2]; // U16
447 uint8_t max_track_points[4]; // U32
448 uint8_t max_routes[2]; // U16
449 uint8_t max_route_points[4]; // U32
450 uint8_t max_route_shape_points[4]; // U32
451 uint8_t max_maps[2]; // U16
452 uint8_t min_map_version[2]; // U16
453 uint8_t max_map_version[2]; // U16
454 uint8_t total_internal_file_memory[4]; // U32
455 uint8_t avail_internal_file_memory[4]; // U32
456 uint8_t total_external_file_memory[4]; // U32
457 uint8_t avail_external_file_memory[4]; // U32
458 } msg_capabilities_t;
459
460 //-----------------------------------------------------------------------------
461
462 #if __APPLE__ || __linux
463 #include <sys/time.h>
464 #endif
465
466 static void
debug_out(const char * fmt,...)467 debug_out(const char* fmt, ...)
468 {
469 va_list ap;
470 va_start(ap, fmt);
471 fputs(MYNAME ": ", stderr);
472 vfprintf(stderr, fmt, ap);
473 va_end(ap);
474 }
475
476 static void
debug_out_time(const char * s)477 debug_out_time(const char* s)
478 {
479 #if __APPLE__ || __linux
480 struct timeval tv;
481 gettimeofday(&tv, NULL);
482 debug_out("%u.%03u %s", (unsigned)tv.tv_sec & 0xf, (unsigned)tv.tv_usec / 1000, s);
483 #else
484 debug_out("%u %s", (unsigned)time(NULL) & 0xf, s);
485 #endif
486 }
487
488 //-----------------------------------------------------------------------------
489
490 static uint16_t
checksum(const uint8_t * p,unsigned n)491 checksum(const uint8_t* p, unsigned n)
492 {
493 int x = 0;
494 unsigned i;
495 for (i = n / 2; i > 0; i--) {
496 x += *p++;
497 x += *p++ << 8;
498 }
499 if (n & 1) {
500 x += *p;
501 }
502 return (uint16_t)-x;
503 }
504
505 //-----------------------------------------------------------------------------
506 // OS packet read/write wrappers
507
508 static unsigned
packet_read(void * buf)509 packet_read(void* buf)
510 {
511 unsigned n = delbin_os_ops.packet_read(buf);
512 if (n == 0) {
513 fatal(MYNAME ": read 0\n");
514 }
515 if (global_opts.debug_level >= DBGLVL_H) {
516 unsigned j;
517 const uint8_t* p = (const uint8_t*) buf;
518
519 debug_out_time("pcktrd");
520 for (j = 0; j < n; j++) {
521 warning(" %02x", p[j]);
522 }
523 if (global_opts.debug_level >= DBGLVL_H2) {
524 warning(" ");
525 for (j = 0; j < n; j++) {
526 int c = p[j];
527 warning("%c", isprint(c) ? c : '.');
528 }
529 }
530 warning("\n");
531 }
532 return n;
533 }
534
535 static void
packet_write(const void * buf,unsigned size)536 packet_write(const void* buf, unsigned size)
537 {
538 unsigned n;
539 if (global_opts.debug_level >= DBGLVL_H) {
540 unsigned j;
541 const uint8_t* p = (const uint8_t*) buf;
542
543 debug_out_time("pcktwr");
544 for (j = 0; j < size; j++) {
545 warning(" %02x", p[j]);
546 }
547 if (global_opts.debug_level >= DBGLVL_H2) {
548 warning(" ");
549 for (j = 0; j < size; j++) {
550 int c = p[j];
551 warning("%c", isprint(c) ? c : '.');
552 }
553 }
554 warning("\n");
555 }
556 n = delbin_os_ops.packet_write(buf, size);
557 if (n != size) {
558 fatal(MYNAME ": short write %u %u\n", size, n);
559 }
560 }
561
562 //-----------------------------------------------------------------------------
563
564 // dynamically sized buffer with space reserved for message header and trailer
565 typedef struct {
566 // message data size
567 unsigned size;
568 // buffer size
569 unsigned capacity;
570 uint8_t* buf;
571 // convenience pointer to message data area
572 void* data;
573 } message_t;
574
575 static void
message_init(message_t * m)576 message_init(message_t* m)
577 {
578 m->capacity = 100;
579 m->buf = (uint8_t*)xmalloc(m->capacity);
580 m->data = m->buf + 2 + 8;
581 }
582
583 static void
message_init_size(message_t * m,unsigned size)584 message_init_size(message_t* m, unsigned size)
585 {
586 m->size = size;
587 m->capacity = 2 + 8 + size + 4;
588 m->buf = (uint8_t*)xmalloc(m->capacity);
589 m->data = m->buf + 2 + 8;
590 }
591
592 static void
message_free(message_t * m)593 message_free(message_t* m)
594 {
595 xfree(m->buf);
596 m->buf = NULL;
597 m->data = NULL;
598 }
599
600 static void
message_ensure_size(message_t * m,unsigned size)601 message_ensure_size(message_t* m, unsigned size)
602 {
603 m->size = size;
604 if (m->capacity < 2 + 8 + size + 4) {
605 m->capacity = 2 + 8 + size + 4;
606 xfree(m->buf);
607 m->buf = (uint8_t*)xmalloc(m->capacity);
608 m->data = m->buf + 2 + 8;
609 }
610 }
611
612 static unsigned
message_get_id(const message_t * m)613 message_get_id(const message_t* m)
614 {
615 return le_readu16(m->buf + 4);
616 }
617
618 //-----------------------------------------------------------------------------
619
620 static void
message_write(unsigned msg_id,message_t * m)621 message_write(unsigned msg_id, message_t* m)
622 {
623 unsigned chksum;
624 unsigned count;
625 unsigned n;
626 uint8_t* p = m->buf;
627
628 // header (2 start bytes filled in later)
629 p[2] = 0xdb;
630 p[3] = 0xfe;
631 le_write16(p + 4, msg_id);
632 // "data size" includes 4 trailer bytes
633 le_write16(p + 6, m->size + 4);
634 chksum = checksum(p + 2, 6);
635 le_write16(p + 8, chksum);
636 // message data (filled in by caller)
637 chksum = checksum((uint8_t*) m->data, m->size);
638 n = 2 + 8 + m->size;
639 // trailer (checksum and marker bytes)
640 le_write16(p + n, chksum);
641 p[n + 2] = 0xad;
642 p[n + 3] = 0xbc;
643 // size of message not counting packet start bytes
644 count = 8 + m->size + 4;
645 do {
646 const uint8_t save0 = p[0];
647 const uint8_t save1 = p[1];
648 n = delbin_os_packet_size - 2;
649 if (n > count) {
650 n = count;
651 }
652 // doc. says 0x20, device sends 0, probably ignored
653 p[0] = 0x20;
654 // valid bytes in packet after first 2
655 p[1] = n;
656 packet_write(p, 2 + n);
657 p[0] = save0;
658 p[1] = save1;
659 p += n;
660 count -= n;
661 } while (count != 0);
662 if (global_opts.debug_level >= DBGLVL_M) {
663 warning(MYNAME ": sent %x\n", msg_id);
664 }
665 }
666
667 // read from the payload of a single packet
668 static unsigned
read_depacketize_1(uint8_t ** p,unsigned n,int new_packet)669 read_depacketize_1(uint8_t** p, unsigned n, int new_packet)
670 {
671 static uint8_t buf[256];
672 static unsigned buf_i, buf_n;
673 if (new_packet) {
674 buf_n = 0;
675 }
676 while (buf_n == 0) {
677 packet_read(buf);
678 if (buf[1] <= delbin_os_packet_size - 2) {
679 buf_n = buf[1];
680 buf_i = 2;
681 }
682 }
683 *p = buf + buf_i;
684 if (n > buf_n) {
685 n = buf_n;
686 }
687 buf_n -= n;
688 buf_i += n;
689 return n;
690 }
691
692 // read from packet payloads until request is fulfilled
693 static void
read_depacketize(uint8_t * buf,unsigned n)694 read_depacketize(uint8_t* buf, unsigned n)
695 {
696 while (n) {
697 uint8_t* p;
698 unsigned nn = read_depacketize_1(&p, n, FALSE);
699 memcpy(buf, p, nn);
700 n -= nn;
701 buf += nn;
702 }
703 }
704
705 // Get one valid message.
706 // If a corrupted message with the right id is seen, return failure (0).
707 static unsigned
message_read_1(unsigned msg_id,message_t * m)708 message_read_1(unsigned msg_id, message_t* m)
709 {
710 unsigned id;
711 for (;;) {
712 unsigned total;
713 unsigned n;
714 uint8_t buf[8];
715 uint8_t* p;
716
717 n = read_depacketize_1(&p, 8, FALSE);
718 memset(buf, 0, 8);
719 memcpy(buf, p, n);
720 while (buf[0] != 0xdb || buf[1] != 0xfe || checksum(buf, 6) != le_readu16(buf + 6)) {
721 // try for a message start at the beginning of next packet
722 n = read_depacketize_1(&p, 8, TRUE);
723 memset(buf, 0, 8);
724 memcpy(buf, p, n);
725 }
726 id = le_readu16(buf + 2);
727 total = le_readu16(buf + 4);
728 message_ensure_size(m, total - 4);
729 // copy in message head, really only need id field, do the rest for debugging
730 m->buf[0] = m->buf[1] = 0;
731 memcpy(m->buf + 2, buf, 8);
732 // read message body and trailer
733 read_depacketize((uint8_t*) m->data, total);
734 p = (uint8_t*)m->data + m->size;
735 if (checksum((uint8_t*) m->data, m->size) == le_readu16(p) &&
736 p[2] == 0xad && p[3] == 0xbc) {
737 if (global_opts.debug_level >= DBGLVL_M) {
738 warning(MYNAME ": received %x\n", id);
739 }
740 break;
741 }
742 if (global_opts.debug_level >= DBGLVL_L) {
743 warning(MYNAME ": corrupted message %x\n", id);
744 }
745 if (id == msg_id) {
746 id = 0;
747 break;
748 }
749 }
750 return id;
751 }
752
753 // Send MSG_ACK for given message
754 static void
message_ack(unsigned id,const message_t * m)755 message_ack(unsigned id, const message_t* m)
756 {
757 message_t ack;
758 char* p1;
759 const char* p2 = (const char*) m->data;
760 switch (id) {
761 case MSG_ACK:
762 case MSG_NACK:
763 case MSG_NAVIGATION:
764 case MSG_SATELLITE_INFO:
765 // don't ack these
766 return;
767 }
768 message_init_size(&ack, 4);
769 p1 = (char*) ack.data;
770 // ack payload is id and body checksum of acked message
771 le_write16(p1, id);
772 p1[2] = p2[m->size];
773 p1[3] = p2[m->size + 1];
774 message_write(MSG_ACK, &ack);
775 message_free(&ack);
776 }
777
778 // Get specific message, ignoring others. Sends ACK for non-interval messages.
779 // Gives up after at least READ_TIMEOUT-1 seconds have passed.
780 static int
message_read(unsigned msg_id,message_t * m)781 message_read(unsigned msg_id, message_t* m)
782 {
783 unsigned id;
784 time_t time_start = time(NULL);
785
786 if (global_opts.debug_level >= DBGLVL_M) {
787 warning(MYNAME ": looking for %x\n", msg_id);
788 }
789 for (;;) {
790 id = message_read_1(msg_id, m);
791 if (id == 0) {
792 break;
793 }
794 if (id == MSG_ERROR) {
795 const uint8_t* p = (const uint8_t*) m->data;
796 fatal(MYNAME ": device error %u: \"%s\"\n", *p, p + 1);
797 }
798 message_ack(id, m);
799 if (id == msg_id || time(NULL) - time_start >= READ_TIMEOUT) {
800 break;
801 }
802 }
803 return id == msg_id;
804 }
805
806 // Read a sequence of messages, up to a MSG_TRANSFER_COMPLETE
807 static int
get_batch(message_t ** array,unsigned * n)808 get_batch(message_t** array, unsigned* n)
809 {
810 int success = 1;
811 unsigned array_max = 100;
812 message_t* a = (message_t*) xmalloc(array_max * sizeof(message_t));
813 unsigned i = 0;
814 unsigned id;
815 if (global_opts.debug_level >= DBGLVL_M) {
816 warning(MYNAME ": begin get_batch\n");
817 }
818 do {
819 time_t time_start = time(NULL);
820 if (i == array_max) {
821 message_t* old_a = a;
822 array_max += array_max;
823 a = (message_t*) xmalloc(array_max * sizeof(message_t));
824 memcpy(a, old_a, i * sizeof(message_t));
825 xfree(old_a);
826 }
827 message_init(&a[i]);
828 for (;;) {
829 id = message_read_1(0, &a[i]);
830 switch (id) {
831 case MSG_NAVIGATION:
832 if (time(NULL) - time_start >= READ_TIMEOUT) {
833 success = 0;
834 break;
835 }
836 // fall through
837 case MSG_ACK:
838 case MSG_NACK:
839 case MSG_SATELLITE_INFO:
840 continue;
841 }
842 break;
843 }
844 message_ack(id, &a[i]);
845 i++;
846 } while (success && id != MSG_TRANSFER_COMPLETE);
847 if (success) {
848 *array = a;
849 *n = i - 1;
850 message_free(&a[*n]);
851 if (global_opts.debug_level >= DBGLVL_M) {
852 warning(MYNAME ": end get_batch, %u messages\n", *n);
853 }
854 } else {
855 while (i--) {
856 message_free(&a[i]);
857 }
858 xfree(a);
859 *array = NULL;
860 *n = 0;
861 if (global_opts.debug_level >= DBGLVL_M) {
862 warning(MYNAME ": end get_batch, failed\n");
863 }
864 }
865 return success;
866 }
867
868 typedef struct {
869 unsigned msg_id;
870 message_t msg;
871 } batch_array_t;
872
873 static batch_array_t* batch_array;
874
875 static unsigned batch_array_max;
876 static unsigned batch_array_i;
877
878 // add a message to sequence that will later be sent all at once
879 static void
add_to_batch(unsigned id,message_t * m)880 add_to_batch(unsigned id, message_t* m)
881 {
882 if (batch_array_i == batch_array_max) {
883 char* old = (char*) batch_array;
884 if (batch_array_max == 0) {
885 batch_array_max = 50;
886 }
887 batch_array_max += batch_array_max;
888 batch_array = (batch_array_t*) xmalloc(batch_array_max * sizeof(*batch_array));
889 if (batch_array_i) {
890 memcpy(batch_array, old, batch_array_i * sizeof(*batch_array));
891 xfree(old);
892 }
893 }
894 batch_array[batch_array_i].msg_id = id;
895 batch_array[batch_array_i].msg = *m;
896 batch_array_i++;
897 memset(m, 0, sizeof(*m));
898 }
899
900 // send an accumulated sequence of messages
901 static void
send_batch(void)902 send_batch(void)
903 {
904 message_t m;
905 const unsigned n = batch_array_i;
906 unsigned i;
907 unsigned progress = 0;
908
909 message_init(&m);
910 if (global_opts.debug_level >= DBGLVL_M) {
911 warning(MYNAME ": begin send_batch, %u messages\n", n);
912 }
913 for (i = 0; i < n; i++) {
914 unsigned timeout_count = 0;
915 time_t time_start = time(NULL);
916
917 // Can't really trigger this off either i or n as we don't
918 // know how the various packets map to actual waypts.
919 if (global_opts.verbose_status &&
920 (batch_array[i].msg_id == MSG_WAYPOINT_IN)) {
921 waypt_status_disp(waypoint_n, ++progress);
922 }
923
924 message_write(batch_array[i].msg_id, &batch_array[i].msg);
925 for (;;) {
926 unsigned id = message_read_1(0, &m);
927 switch (id) {
928 case MSG_ACK:
929 break;
930 case MSG_NAVIGATION:
931 if (time(NULL) - time_start >= 2) {
932 if (timeout_count) {
933 fatal(MYNAME ": send_batch timed out\n");
934 }
935 timeout_count++;
936 if (global_opts.debug_level >= DBGLVL_M) {
937 warning(MYNAME ": re-sending %x\n", batch_array[i].msg_id);
938 }
939 message_write(batch_array[i].msg_id, &batch_array[i].msg);
940 time_start = time(NULL);
941 }
942 // fall through
943 case MSG_NACK:
944 case MSG_SATELLITE_INFO:
945 continue;
946 default:
947 warning(MYNAME ": unexpected response message %x during send_batch\n", id);
948 continue;
949 }
950 break;
951 }
952 }
953 message_read(MSG_TRANSFER_COMPLETE, &m);
954 if (global_opts.debug_level >= DBGLVL_M) {
955 warning(MYNAME ": end send_batch\n");
956 }
957 for (i = n; i--;) {
958 message_free(&batch_array[i].msg);
959 }
960 xfree(batch_array);
961 message_free(&m);
962 batch_array_i = batch_array_max = 0;
963 }
964
965 //-----------------------------------------------------------------------------
966 // Coordinate conversion
967
968 static double
delbin_rad2deg(int32_t x)969 delbin_rad2deg(int32_t x)
970 {
971 return x * ((180 / M_PI) / 100000000);
972 }
973
974 static int32_t
delbin_deg2rad(double x)975 delbin_deg2rad(double x)
976 {
977 return (int32_t)(x * ((M_PI / 180) * 100000000));
978 }
979
980 //-----------------------------------------------------------------------------
981 // Waypoint reading
982
983 static time_t
decode_time(const uint8_t * p)984 decode_time(const uint8_t* p)
985 {
986 struct tm t;
987 t.tm_year = p[0];
988 t.tm_mon = p[1] - 1;
989 t.tm_mday = p[2];
990 t.tm_hour = p[3];
991 t.tm_min = p[4];
992 t.tm_sec = p[5];
993 return mkgmtime(&t);
994 }
995
996 static Waypoint*
decode_waypoint(const void * data)997 decode_waypoint(const void* data)
998 {
999 Waypoint* wp = new Waypoint;
1000 const msg_waypoint_t* p = (const msg_waypoint_t*)data;
1001 const char* s;
1002 float f;
1003
1004 wp->SetCreationTime(decode_time(&p->year));
1005 wp->latitude = delbin_rad2deg(le_read32(p->latitude));
1006 wp->longitude = delbin_rad2deg(le_read32(p->longitude));
1007 f = le_read_float(p->elevation);
1008 if (f > UNKNOWN_ELEV) {
1009 wp->altitude = f;
1010 }
1011 wp->icon_descr = waypoint_symbol(p->symbol);
1012 // if (!wp->icon_descr.isNull()) {
1013 // wp->icon_descr = wp->icon_descr;
1014 // }
1015 if (p->name_size && p->name[0]) {
1016 wp->description = p->name;
1017 }
1018 s = p->name + p->name_size;
1019 if (le_readu16(s) && s[2]) {
1020 wp->notes = xstrdup(s + 2);
1021 }
1022 return wp;
1023 }
1024
1025 static void
read_waypoints(void)1026 read_waypoints(void)
1027 {
1028 message_t m;
1029 message_t* msg_array;
1030 unsigned msg_array_n;
1031 Waypoint* wp = NULL;
1032 unsigned n_point;
1033 unsigned notes_i = 0;
1034 unsigned notes_max = 0;
1035 unsigned i;
1036 int attempt = ATTEMPT_MAX;
1037
1038 message_init(&m);
1039 // get number of waypoints
1040 for (;;) {
1041 m.size = 0;
1042 message_write(MSG_WAYPOINT_COUNT, &m);
1043 if (message_read(MSG_WAYPOINT_COUNT, &m)) {
1044 break;
1045 }
1046 if (--attempt == 0) {
1047 fatal(MYNAME ": reading waypoint count failed\n");
1048 }
1049 }
1050 n_point = le_readu32(m.data);
1051 if (global_opts.debug_level >= DBGLVL_L) {
1052 warning(MYNAME ": %u waypoints\n", n_point);
1053 }
1054 if (n_point == 0) {
1055 message_free(&m);
1056 return;
1057 }
1058 // get waypoint messages
1059 attempt = ATTEMPT_MAX;
1060 for (;;) {
1061 m.size = MSG_REQUEST_WAYPOINTS_SIZE;
1062 memset(m.data, 0, m.size);
1063 // This byte is documented as reserved. Setting it to 3 is required to get
1064 // extended notes (message 0xb015) with PN-40 firmware 2.5.
1065 // Whether it has any effect with earlier firmware or the PN-20 is unknown.
1066 ((char*)m.data)[1] = 3;
1067 message_write(MSG_REQUEST_WAYPOINTS, &m);
1068 if (get_batch(&msg_array, &msg_array_n)) {
1069 break;
1070 }
1071 if (--attempt == 0) {
1072 fatal(MYNAME ": reading waypoints failed\n");
1073 }
1074 if (global_opts.debug_level >= DBGLVL_M) {
1075 warning(MYNAME ": timed out reading waypoints, retrying\n");
1076 }
1077 m.size = MSG_BREAK_SIZE;
1078 memset(m.data, 0, m.size);
1079 message_write(MSG_BREAK, &m);
1080 }
1081 message_free(&m);
1082 // process waypoint messages
1083 for (i = 0; i < msg_array_n; i++) {
1084 unsigned id = message_get_id(&msg_array[i]);
1085 if (id == MSG_WAYPOINT_OUT) {
1086 wp = decode_waypoint(msg_array[i].data);
1087 waypt_add(wp);
1088 notes_i = 0;
1089 notes_max = 0;
1090 if (global_opts.debug_level >= DBGLVL_L) {
1091 warning(MYNAME ": read waypoint '%s'\n", qPrintable(wp->description));
1092 }
1093 } else if (wp && id == MSG_WAYPOINT_NOTE_OUT) {
1094 const msg_waypoint_note_t* p = (const msg_waypoint_note_t*) msg_array[i].data;
1095 const char* s = p->name + p->name_size;
1096 unsigned nn = le_readu16(s);
1097 if (notes_max < notes_i + nn) {
1098 #if NEW_STRINGS
1099 // This section needs a serious rethinking.
1100 #else
1101 char* old = wp->notes;
1102 #endif
1103 if (notes_max == 0) {
1104 notes_max = nn;
1105 }
1106 do {
1107 notes_max += notes_max;
1108 } while (notes_max < notes_i + nn);
1109 wp->notes = (char*) xmalloc(notes_max);
1110 #if NEW_STRINGS
1111 #else
1112 if (old) {
1113 memcpy(wp->notes, old, notes_i);
1114 xfree(old);
1115 }
1116 #endif
1117 }
1118 if (nn) {
1119 #if NEW_STRINGS
1120 // Is this really what this code was trying to do?
1121 wp->notes += QString::fromUtf8(s + 2, nn);
1122 #else
1123 memcpy(wp->notes + notes_i, s + 2, nn);
1124 #endif
1125 notes_i += nn;
1126 if (wp->notes[notes_i - 1] == 0) {
1127 notes_i--;
1128 }
1129 }
1130 } else {
1131 fatal(MYNAME ": unexpected message %x while reading waypoints\n", id);
1132 }
1133 message_free(&msg_array[i]);
1134 }
1135 xfree(msg_array);
1136 }
1137
1138 //-----------------------------------------------------------------------------
1139 // Waypoint writing
1140
1141 static void
encode_time(time_t time_,uint8_t * p)1142 encode_time(time_t time_, uint8_t* p)
1143 {
1144 const struct tm* t = gmtime(&time_);
1145 p[0] = t->tm_year;
1146 p[1] = t->tm_mon + 1;
1147 p[2] = t->tm_mday;
1148 p[3] = t->tm_hour;
1149 p[4] = t->tm_min;
1150 p[5] = t->tm_sec;
1151 }
1152
1153 static void
get_gc_notes(const Waypoint * wp,int * symbol,char ** notes,unsigned * notes_size)1154 get_gc_notes(const Waypoint* wp, int* symbol, char** notes, unsigned* notes_size)
1155 {
1156 fs_xml* fs_gpx;
1157 xml_tag* root = NULL;
1158 gbfile* fd = gbfopen(NULL, "w", MYNAME);
1159 const char* size = NULL;
1160 int gc_sym = 0;
1161
1162 switch (wp->gc_data->type) {
1163 case gt_traditional:
1164 gc_sym = 160;
1165 break;
1166 case gt_multi:
1167 gc_sym = 161;
1168 break;
1169 case gt_virtual:
1170 gc_sym = 169;
1171 break;
1172 case gt_letterbox:
1173 gc_sym = 163;
1174 break;
1175 case gt_event:
1176 gc_sym = 165;
1177 break;
1178 case gt_suprise:
1179 gc_sym = 162;
1180 break;
1181 case gt_webcam:
1182 gc_sym = 170;
1183 break;
1184 case gt_earth:
1185 gc_sym = 168;
1186 break;
1187 case gt_benchmark:
1188 gc_sym = 172;
1189 break;
1190 case gt_cito:
1191 gc_sym = 167;
1192 break;
1193 case gt_mega:
1194 gc_sym = 166;
1195 break;
1196 case gt_wherigo:
1197 gc_sym = 164;
1198 break;
1199 case gt_unknown:
1200 case gt_locationless:
1201 case gt_ape:
1202 break;
1203 }
1204 if (0 == (wp->icon_descr.compare("Geocache Found"))) {
1205 gc_sym = 124;
1206 }
1207 if (!wp->description.isEmpty()) {
1208 gbfputs(wp->description, fd);
1209 if (!wp->gc_data->placer.isEmpty()) {
1210 gbfprintf(fd, " by %s", CSTR(wp->gc_data->placer));
1211 }
1212 gbfputc('\n', fd);
1213 }
1214
1215 gbfprintf(fd, "Cache ID: %s\n", CSTRc(wp->shortname));
1216 if (gc_sym && opt_gcsym && atoi(opt_gcsym)) {
1217 gbfprintf(fd, "%s\n", waypoint_symbol(gc_sym));
1218 *symbol = gc_sym;
1219 } else if (!wp->icon_descr.isNull()) {
1220 gbfprintf(fd, "%s\n", CSTR(wp->icon_descr));
1221 }
1222 switch (wp->gc_data->container) {
1223 case gc_micro:
1224 size = "Micro";
1225 break;
1226 case gc_small:
1227 size = "Small";
1228 break;
1229 case gc_regular:
1230 size = "Regular";
1231 break;
1232 case gc_large:
1233 size = "Large";
1234 break;
1235 case gc_unknown:
1236 size = "Not Chosen" ;
1237 break;
1238 case gc_other:
1239 size = "Other";
1240 break;
1241 // Device has no symbol for this, but this is what Topo sends.
1242 case gc_virtual:
1243 size = "Virtual";
1244 break;
1245 default:
1246 break;
1247 }
1248 if (size) {
1249 gbfprintf(fd, "SIZE: %s\n", size);
1250 }
1251 if (wp->gc_data->diff % 10) {
1252 gbfprintf(fd, "D%.1f", wp->gc_data->diff / 10.0);
1253 } else {
1254 gbfprintf(fd, "D%u", wp->gc_data->diff / 10);
1255 }
1256 if (wp->gc_data->terr % 10) {
1257 gbfprintf(fd, "/T%.1f\n", wp->gc_data->terr / 10.0);
1258 } else {
1259 gbfprintf(fd, "/T%u\n", wp->gc_data->terr / 10);
1260 }
1261 if (!wp->gc_data->hint.isEmpty() && !opt_hint_at_end) {
1262 gbfprintf(fd, "HINT: %s\n", CSTR(wp->gc_data->hint));
1263 }
1264 if (!wp->gc_data->desc_short.utfstring.isEmpty() || !wp->gc_data->desc_long.utfstring.isEmpty()) {
1265 gbfputs("DESC: ", fd);
1266 if (!wp->gc_data->desc_short.utfstring.isEmpty()) {
1267 char* s1 = strip_html(&wp->gc_data->desc_short);
1268 char* s2 = cet_str_utf8_to_any(s1, global_opts.charset);
1269 gbfprintf(fd, "%s\n", s2);
1270 xfree(s2);
1271 xfree(s1);
1272 }
1273 if (!wp->gc_data->desc_long.utfstring.isEmpty()) {
1274 char* s1 = strip_html(&wp->gc_data->desc_long);
1275 char* s2 = cet_str_utf8_to_any(s1, global_opts.charset);
1276 gbfputs(s2, fd);
1277 xfree(s2);
1278 xfree(s1);
1279 }
1280 }
1281 fs_gpx = (fs_xml*)fs_chain_find(wp->fs, FS_GPX);
1282 if (opt_logs && fs_gpx && fs_gpx->tag) {
1283 root = xml_findfirst(fs_gpx->tag, "groundspeak:logs");
1284 }
1285 if (root) {
1286 xml_tag* curlog = xml_findfirst(root, "groundspeak:log");
1287 if (curlog) {
1288 gbfputs("\nLOG:\n", fd);
1289 }
1290 for (; curlog; curlog = xml_findnext(root, curlog, "groundspeak:log")) {
1291 xml_tag* logpart = xml_findfirst(curlog, "groundspeak:type");
1292 if (logpart) {
1293 gbfprintf(fd, "%s\n", CSTR(logpart->cdata));
1294 }
1295 logpart = xml_findfirst(curlog, "groundspeak:date");
1296 if (logpart) {
1297 time_t logtime = xml_parse_time(logpart->cdata).toTime_t();
1298 const struct tm* logtm = gmtime(&logtime);
1299 gbfprintf(fd, "%d-%02d-%02d ", logtm->tm_year + 1900, logtm->tm_mon + 1, logtm->tm_mday);
1300 }
1301 logpart = xml_findfirst(curlog, "groundspeak:finder");
1302 if (logpart) {
1303 char* s = cet_str_utf8_to_any(CSTR(logpart->cdata), global_opts.charset);
1304 gbfputs(s, fd);
1305 xfree(s);
1306 }
1307 logpart = xml_findfirst(curlog, "groundspeak:text");
1308 if (logpart) {
1309 char* s = cet_str_utf8_to_any(CSTR(logpart->cdata), global_opts.charset);
1310 gbfprintf(fd, ", %s", s);
1311 xfree(s);
1312 }
1313 gbfputc('\n', fd);
1314 }
1315 }
1316 if (!wp->gc_data->hint.isEmpty() && opt_hint_at_end) {
1317 gbfprintf(fd, "\nHINT: %s\n", CSTR(wp->gc_data->hint));
1318 }
1319 gbfputc(0, fd);
1320 *notes_size = fd->memlen;
1321 *notes = (char*) xmalloc(*notes_size);
1322 memcpy(*notes, fd->handle.mem, *notes_size);
1323 gbfclose(fd);
1324 }
1325
1326 static void
write_waypoint_notes(const char * notes,unsigned size,const char * name)1327 write_waypoint_notes(const char* notes, unsigned size, const char* name)
1328 {
1329 message_t m;
1330 const unsigned name_size = strlen(name) + 1;
1331 const unsigned bytes_per_msg = (10 * (delbin_os_packet_size - 2)) - name_size - 20;
1332 const unsigned msg_count = (size + (bytes_per_msg - 1)) / bytes_per_msg;
1333 unsigned i = 1;
1334
1335 do {
1336 char* pp;
1337 unsigned n = bytes_per_msg;
1338 msg_waypoint_note_t* p;
1339 message_init_size(&m, 2 + 2 + 1 + name_size + 2 + bytes_per_msg);
1340 p = (msg_waypoint_note_t*) m.data;
1341 le_write16(p->index, i++);
1342 le_write16(p->total, msg_count);
1343 p->name_size = name_size;
1344 memcpy(p->name, name, p->name_size);
1345 pp = p->name + p->name_size;
1346 if (n > size) {
1347 n = size;
1348 }
1349 le_write16(pp, n);
1350 pp += 2;
1351 memcpy(pp, notes, n);
1352 pp += n;
1353 if (*(pp - 1)) {
1354 *pp++ = 0;
1355 }
1356 notes += n;
1357 size -= n;
1358 m.size = pp - (char*)p;
1359 add_to_batch(MSG_WAYPOINT_NOTE_IN, &m);
1360 } while (size != 0);
1361 }
1362
1363 static void
add_nuke(nuke_type type)1364 add_nuke(nuke_type type)
1365 {
1366 message_t m;
1367 msg_delete_t* p;
1368
1369 message_init_size(&m, MSG_DELETE_SIZE);
1370 p = (msg_delete_t*) m.data;
1371 p->type = type;
1372 p->mode = nuke_mode_all;
1373 p->location = nuke_dest_internal;
1374 memset(p->object_name, 0, sizeof(p->object_name));
1375
1376 // MSG_DELETE generates a MSG_TRANSFER_COMPLETE,
1377 // so use the batch facility to wait for it
1378 add_to_batch(MSG_DELETE, &m);
1379 send_batch();
1380 }
1381
1382 static void
write_waypoint(const Waypoint * wp)1383 write_waypoint(const Waypoint* wp)
1384 {
1385 message_t m;
1386 msg_waypoint_t* p;
1387 QString name = wp->shortname;
1388 char* notes;
1389 unsigned name_size;
1390 unsigned notes_size = 0;
1391 unsigned extended_notes_size = 0;
1392 const char* notes_freeable = NULL;
1393 int symbol = -1;
1394 float elev = UNKNOWN_ELEV;
1395 char* pp;
1396
1397 if (wp->EmptyGCData()) {
1398 notes = xstrdup(wp->notes);
1399 if (notes == NULL && wp->description.isEmpty() && wp->shortname != wp->description) {
1400 notes = xstrdup(wp->description);
1401 }
1402 if (notes) {
1403 notes_size = strlen(notes) + 1;
1404 }
1405 } else {
1406 get_gc_notes(wp, &symbol, ¬es, ¬es_size);
1407 notes_freeable = notes;
1408 if (!wp->description.isEmpty()) {
1409 name = mkshort(mkshort_handle, wp->description);
1410 }
1411 }
1412
1413 if (notes_size > 800) {
1414 if (use_extended_notes) {
1415 extended_notes_size = notes_size;
1416 notes_size = 1;
1417 } else {
1418 notes_size = 800;
1419 }
1420 }
1421
1422 name_size = strlen(CSTRc(name)) + 1;
1423 if (name_size > 255) {
1424 name_size = 255;
1425 }
1426 message_init_size(&m, 31 + name_size + notes_size);
1427 p = (msg_waypoint_t*) m.data;
1428
1429 waypoint_i++;
1430 le_write32(p->total, waypoint_n);
1431 le_write32(p->index, waypoint_i);
1432 encode_time(wp->GetCreationTime().toTime_t(), &p->year);
1433 le_write32(p->latitude, delbin_deg2rad(wp->latitude));
1434 le_write32(p->longitude, delbin_deg2rad(wp->longitude));
1435 if (wp->altitude > unknown_alt) {
1436 elev = wp->altitude;
1437 }
1438 le_write_float(p->elevation, elev);
1439 if (symbol < 0) {
1440 symbol = 0;
1441 if (!wp->icon_descr.isNull()) {
1442 symbol = waypoint_symbol_index(CSTR(wp->icon_descr));
1443 }
1444 }
1445 p->symbol = symbol;
1446 p->name_size = name_size;
1447 memcpy(p->name, CSTRc(name), name_size - 1);
1448 p->name[name_size - 1] = 0;
1449 pp = p->name + name_size;
1450 m.size = (pp + 2 + notes_size) - (char*)p;
1451 if (extended_notes_size) {
1452 le_write16(pp, 0xffff);
1453 pp[2] = 0;
1454 } else {
1455 le_write16(pp, notes_size);
1456 #if NEW_STRINGS
1457 #else
1458 if (notes) {
1459 memcpy(pp + 2, notes, notes_size - 1);
1460 pp[2 + notes_size - 1] = 0;
1461 }
1462 #endif
1463 }
1464
1465 add_to_batch(MSG_WAYPOINT_IN, &m);
1466
1467 if (extended_notes_size) {
1468 write_waypoint_notes(notes, extended_notes_size, CSTRc(name));
1469 }
1470 if (notes_freeable) {
1471 xfree(notes_freeable);
1472 }
1473 if (global_opts.debug_level >= DBGLVL_L) {
1474 warning(MYNAME ": wrote waypoint %u '%s'\n", waypoint_i, qPrintable(name));
1475 }
1476 }
1477
1478 static void
write_waypoints(void)1479 write_waypoints(void)
1480 {
1481 message_t m;
1482 unsigned device_n = 0;
1483
1484 waypoint_i = 0;
1485 waypoint_n = waypt_count();
1486 if (waypoint_n > device_max_waypoint) {
1487 fatal(MYNAME ": waypoint count (%u) exceeds device limit (%u)\n",
1488 waypoint_n, device_max_waypoint);
1489 }
1490
1491 message_init_size(&m, 0);
1492 message_write(MSG_WAYPOINT_COUNT, &m);
1493 if (message_read(MSG_WAYPOINT_COUNT, &m)) {
1494 device_n = le_readu32(m.data);
1495 }
1496
1497 waypt_disp_all(write_waypoint);
1498 send_batch();
1499
1500 if (device_n + waypoint_n > device_max_waypoint) {
1501 m.size = 0;
1502 message_write(MSG_WAYPOINT_COUNT, &m);
1503 if (message_read(MSG_WAYPOINT_COUNT, &m) &&
1504 le_readu32(m.data) == device_max_waypoint) {
1505 warning(MYNAME ": waypoint count (%u already on device + %u added = %u)"
1506 " exceeds device limit (%u), some may have been discarded\n",
1507 device_n, waypoint_n, device_n + waypoint_n, device_max_waypoint);
1508 }
1509 }
1510 message_free(&m);
1511 }
1512
1513 //-----------------------------------------------------------------------------
1514 // Track reading
1515
1516 static void
decode_sat_fix(Waypoint * wp,const uint8_t status)1517 decode_sat_fix(Waypoint* wp, const uint8_t status)
1518 {
1519 switch (status & 3) {
1520 case 1:
1521 wp->fix = fix_none;
1522 break;
1523 case 2:
1524 wp->fix = fix_2d;
1525 break;
1526 case 3:
1527 wp->fix = fix_3d;
1528 if (status & 4) {
1529 wp->fix = fix_dgps;
1530 }
1531 break;
1532 }
1533 }
1534
1535 static void
decode_track_point(const void * data,unsigned * wp_array_i,unsigned max_point)1536 decode_track_point(const void* data, unsigned* wp_array_i, unsigned max_point)
1537 {
1538 const msg_track_point_t* p = (const msg_track_point_t*) data;
1539 const unsigned n = p->number;
1540 unsigned i;
1541 unsigned j = *wp_array_i;
1542
1543 if (j + n > max_point) {
1544 fatal(MYNAME ": read too many track points\n");
1545 }
1546 for (i = 0; i < n; i++, j++) {
1547 Waypoint* wp = new Waypoint;
1548 float elev = le_read_float(p->point[i].elevation);
1549 wp_array[j] = wp;
1550 wp->SetCreationTime(decode_time(&p->point[i].year));
1551 wp->latitude = delbin_rad2deg(le_read32(p->point[i].latitude));
1552 wp->longitude = delbin_rad2deg(le_read32(p->point[i].longitude));
1553 if (elev > UNKNOWN_ELEV) {
1554 wp->altitude = elev;
1555 }
1556 wp->speed = le_readu16(p->point[i].speed);
1557 wp->speed *= (100.0f / (60 * 60));
1558 wp->wpt_flags.speed = 1;
1559 decode_sat_fix(wp, p->point[i].status);
1560 wp->wpt_flags.new_trkseg = (p->point[i].status & 0x10) != 0;
1561 }
1562 *wp_array_i = j;
1563 }
1564
1565 static void
read_track(route_head * track)1566 read_track(route_head* track)
1567 {
1568 message_t m;
1569 message_t* msg_array;
1570 const msg_track_header_t* p;
1571 unsigned msg_array_n;
1572 unsigned wp_array_i = 0;
1573 unsigned n_point;
1574 unsigned i;
1575 int attempt = ATTEMPT_MAX;
1576
1577 message_init(&m);
1578 // read track messages
1579 for (;;) {
1580 m.size = MSG_REQUEST_TRACKS_SIZE;
1581 memset(m.data, 0, m.size);
1582 ((char*)m.data)[0] = 1; // Download single track
1583 strcpy((char*)m.data + 1, CSTRc(track->rte_name));
1584 message_write(MSG_REQUEST_TRACKS, &m);
1585 if (get_batch(&msg_array, &msg_array_n)) {
1586 break;
1587 }
1588 if (--attempt == 0) {
1589 fatal(MYNAME ": reading track '%s' failed\n", qPrintable(track->rte_name));
1590 }
1591 if (global_opts.debug_level >= DBGLVL_M) {
1592 warning(MYNAME ": timed out reading track '%s', retrying\n", qPrintable(track->rte_name));
1593 }
1594 m.size = MSG_BREAK_SIZE;
1595 memset(m.data, 0, m.size);
1596 message_write(MSG_BREAK, &m);
1597 }
1598 message_free(&m);
1599 if (msg_array_n == 0 || message_get_id(&msg_array[0]) != MSG_TRACK_HEADER_OUT) {
1600 fatal(MYNAME ": reading track '%s' failed (missing track header)\n", qPrintable(track->rte_name));
1601 }
1602 // process track messages
1603 p = (const msg_track_header_t*) msg_array[0].data;
1604 if (le_readu16(p->comment_size)) {
1605 track->rte_desc = p->comment;
1606 }
1607 track->line_color.bbggrr = track_color(p->color[0]);
1608 n_point = le_readu32(p->total_points);
1609 wp_array = (Waypoint**) xcalloc(n_point, sizeof(*wp_array));
1610 message_free(&msg_array[0]);
1611 for (i = 1; i < msg_array_n; i++) {
1612 unsigned id = message_get_id(&msg_array[i]);
1613 if (id == MSG_TRACK_POINT_OUT) {
1614 decode_track_point(msg_array[i].data, &wp_array_i, n_point);
1615 } else {
1616 fatal(MYNAME ": unexpected message %x while reading track '%s'\n", id, qPrintable(track->rte_name));
1617 }
1618 message_free(&msg_array[i]);
1619 }
1620 xfree(msg_array);
1621 if (n_point != wp_array_i) {
1622 fatal(MYNAME ": track point count mismatch, expected %u, got %u\n", n_point, wp_array_i);
1623 }
1624 if (global_opts.debug_level >= DBGLVL_L) {
1625 warning(MYNAME ": read track '%s' %u points\n", qPrintable(track->rte_name), n_point);
1626 }
1627 for (i = 0; i < n_point; i++) {
1628 track_add_wpt(track, wp_array[i]);
1629 }
1630 track_add_head(track);
1631 xfree(wp_array);
1632 }
1633
1634 static void
read_tracks(void)1635 read_tracks(void)
1636 {
1637 message_t m;
1638 message_t* msg_array;
1639 unsigned msg_array_n;
1640 route_head** track_array;
1641 unsigned total;
1642 unsigned i;
1643 int attempt = ATTEMPT_MAX;
1644
1645 message_init(&m);
1646 // get number of tracks
1647 for (;;) {
1648 m.size = 0;
1649 message_write(MSG_TRACK_COUNT, &m);
1650 if (message_read(MSG_TRACK_COUNT, &m)) {
1651 break;
1652 }
1653 if (--attempt == 0) {
1654 fatal(MYNAME ": reading track count failed\n");
1655 }
1656 }
1657 total = le_readu32(m.data);
1658 if (global_opts.debug_level >= DBGLVL_L) {
1659 warning(MYNAME ": %u tracks\n", total);
1660 }
1661 if (total == 0) {
1662 message_free(&m);
1663 return;
1664 }
1665
1666 // First get track headers, then request each track with non-zero number of points
1667 attempt = ATTEMPT_MAX;
1668 for (;;) {
1669 m.size = MSG_REQUEST_TRACKS_SIZE;
1670 memset(m.data, 0, m.size);
1671 ((char*)m.data)[0] = 2; // Download all track headers
1672 message_write(MSG_REQUEST_TRACKS, &m);
1673 if (get_batch(&msg_array, &msg_array_n)) {
1674 break;
1675 }
1676 if (--attempt == 0) {
1677 fatal(MYNAME ": reading track headers failed\n");
1678 }
1679 if (global_opts.debug_level >= DBGLVL_M) {
1680 warning(MYNAME ": timed out reading track headers, retrying\n");
1681 }
1682 m.size = MSG_BREAK_SIZE;
1683 memset(m.data, 0, m.size);
1684 message_write(MSG_BREAK, &m);
1685 }
1686 message_free(&m);
1687 track_array = (route_head**) xcalloc(total, sizeof(*track_array));
1688 for (i = 0; i < msg_array_n; i++) {
1689 unsigned id = message_get_id(&msg_array[i]);
1690 if (id == MSG_TRACK_HEADER_OUT) {
1691 const msg_track_header_t* p = (msg_track_header_t*) msg_array[i].data;
1692 if (le_readu32(p->total_points)) {
1693 track_array[i] = route_head_alloc();
1694 track_array[i]->rte_name = p->name;
1695 }
1696 } else {
1697 fatal(MYNAME ": unexpected message %x while reading track headers\n", id);
1698 }
1699 message_free(&msg_array[i]);
1700 }
1701 xfree(msg_array);
1702 // get each track
1703 for (i = 0; i < total; i++) {
1704 if (track_array[i]) {
1705 read_track(track_array[i]);
1706 }
1707 }
1708 xfree(track_array);
1709 }
1710
1711 //-----------------------------------------------------------------------------
1712 // Track writing
1713
1714 static void
write_track_points(void)1715 write_track_points(void)
1716 {
1717 message_t m;
1718 const unsigned pt_per_msg = 10;
1719 msg_track_point_t* p = NULL;
1720 unsigned i = 0;
1721 unsigned j = 0;
1722
1723 do {
1724 const Waypoint* wp = wp_array[i];
1725 float f;
1726
1727 if (j == 0) {
1728 message_init_size(&m, 9 + 23 * pt_per_msg);
1729 p =(msg_track_point_t*) m.data;
1730 le_write32(p->total, waypoint_n);
1731 le_write32(p->index, i + 1);
1732 }
1733 assert(p);
1734 encode_time(wp->GetCreationTime().toTime_t(), &p->point[j].year);
1735 le_write32(p->point[j].latitude, delbin_deg2rad(wp->latitude));
1736 le_write32(p->point[j].longitude, delbin_deg2rad(wp->longitude));
1737 f = UNKNOWN_ELEV;
1738 if (wp->altitude > unknown_alt) {
1739 f = wp->altitude;
1740 }
1741 le_write_float(p->point[j].elevation, f);
1742 f = WAYPT_GET(wp, speed, 0);
1743 f *= (60 * 60) / 100;
1744 le_write16(p->point[j].speed, (uint16_t)f);
1745 f = WAYPT_GET(wp, course, 0);
1746 f *= 100;
1747 le_write16(p->point[j].heading, (uint16_t)f);
1748 switch (wp->fix) {
1749 default:
1750 p->point[j].status = 0;
1751 break;
1752 case fix_none:
1753 p->point[j].status = 1;
1754 break;
1755 case fix_2d:
1756 p->point[j].status = 2;
1757 break;
1758 case fix_3d:
1759 p->point[j].status = 3;
1760 break;
1761 case fix_dgps:
1762 p->point[j].status = 4 | 3;
1763 break;
1764 }
1765 if (wp->wpt_flags.new_trkseg) {
1766 p->point[j].status |= 0x10;
1767 }
1768 i++;
1769 j++;
1770 if (j == pt_per_msg || i == waypoint_n) {
1771 p->number = j;
1772 m.size = 9 + 23 * j;
1773 add_to_batch(MSG_TRACK_POINT_IN, &m);
1774 j = 0;
1775 }
1776 } while (i < waypoint_n);
1777 }
1778
1779 static void
write_track_begin(const route_head * track)1780 write_track_begin(const route_head* track)
1781 {
1782 waypoint_i = 0;
1783 waypoint_n = track->rte_waypt_ct;
1784 if (waypoint_n) {
1785 wp_array = (Waypoint**) xmalloc(waypoint_n * sizeof(*wp_array));
1786 }
1787 }
1788
1789 static void
write_track_point(const Waypoint * wp)1790 write_track_point(const Waypoint* wp)
1791 {
1792 wp_array[waypoint_i++] = (Waypoint*)wp;
1793 }
1794
1795 static void
write_track_end(const route_head * track)1796 write_track_end(const route_head* track)
1797 {
1798 message_t m;
1799 msg_track_header_in_t* p;
1800 unsigned comment_size = 0;
1801
1802 if (waypoint_n == 0) {
1803 return;
1804 }
1805 if (!track->rte_desc.isEmpty()) {
1806 comment_size = strlen(CSTRc(track->rte_desc)) + 1;
1807 }
1808 message_init_size(&m, sizeof(msg_track_header_in_t) - 1 + comment_size);
1809 p = (msg_track_header_in_t*) m.data;
1810 memset(p->name, 0, sizeof(p->name));
1811 if (!track->rte_name.isEmpty()) {
1812 strncpy(p->name, CSTRc(track->rte_name), sizeof(p->name) - 1);
1813 } else {
1814 sprintf(p->name, "%lu", (long)wp_array[0]->GetCreationTime().toTime_t());
1815 }
1816 le_write32(p->total_points, waypoint_n);
1817 encode_time(current_time().toTime_t(), &p->year);
1818 le_write16(p->color, track_color_index(track->line_color.bbggrr));
1819 le_write16(p->comment_size, comment_size);
1820 if (comment_size) {
1821 memcpy(p->comment, CSTRc(track->rte_desc), comment_size);
1822 }
1823 add_to_batch(MSG_TRACK_HEADER_IN, &m);
1824 write_track_points();
1825 send_batch();
1826 xfree(wp_array);
1827 }
1828
1829 static void
write_tracks(void)1830 write_tracks(void)
1831 {
1832 track_disp_all(write_track_begin, write_track_end, write_track_point);
1833 }
1834
1835 //-----------------------------------------------------------------------------
1836 // Route reading
1837
1838 static void
decode_route_shape(const void * data,unsigned * wp_array_i)1839 decode_route_shape(const void* data, unsigned* wp_array_i)
1840 {
1841 const msg_route_shape_t* p = (msg_route_shape_t*) data;
1842 const unsigned n = p->number;
1843 unsigned i;
1844 unsigned j = *wp_array_i;
1845
1846 for (i = 0; i < n; i++, j++) {
1847 char buf[32];
1848 Waypoint* wp = new Waypoint;
1849 wp_array[j] = wp;
1850 wp->latitude = delbin_rad2deg(le_read32(p->point[i].latitude));
1851 wp->longitude = delbin_rad2deg(le_read32(p->point[i].longitude));
1852 sprintf(buf, "SHP%03u", j);
1853 wp->shortname = buf;
1854 }
1855 *wp_array_i = j;
1856 }
1857
1858 static Waypoint*
decode_route_point(const void * data)1859 decode_route_point(const void* data)
1860 {
1861 const msg_route_point_t* p = (const msg_route_point_t*) data;
1862 const char* s = NULL;
1863 gbfile* fd = gbfopen(NULL, "w", MYNAME);
1864 Waypoint* wp = new Waypoint;
1865 if (p->name[0]) {
1866 wp->shortname = p->name;
1867 }
1868 // give these a higher priority than the shape points
1869 wp->route_priority = 1;
1870 wp->latitude = delbin_rad2deg(le_read32(p->latitude));
1871 wp->longitude = delbin_rad2deg(le_read32(p->longitude));
1872 switch (p->itinerary_type) {
1873 case 1:
1874 s = "Start";
1875 break;
1876 case 2:
1877 s = "Stop";
1878 break;
1879 case 3:
1880 s = "Finish";
1881 break;
1882 case 4:
1883 s = "Via";
1884 break;
1885 case 5:
1886 s = "Via Hidden";
1887 break;
1888 case 6:
1889 switch (p->turn_type) {
1890 case 1:
1891 s = "Turn, Straight";
1892 break;
1893 case 2:
1894 s = "Turn, Right";
1895 break;
1896 case 3:
1897 s = "Turn, Bear Right";
1898 break;
1899 case 4:
1900 s = "Turn, Keep Right";
1901 break;
1902 case 5:
1903 s = "Turn, Left";
1904 break;
1905 case 6:
1906 s = "Turn, Bear Left";
1907 break;
1908 case 7:
1909 s = "Turn, Keep Left";
1910 break;
1911 case 8:
1912 s = "Turn, Reverse Direction";
1913 break;
1914 case 9:
1915 s = "Turn, Street Name Change";
1916 break;
1917 }
1918 break;
1919 }
1920 if (s) {
1921 gbfprintf(fd, "Type: %s", s);
1922 }
1923 if (p->exit_label_size && p->exit_label[0]) {
1924 gbfprintf(fd, "\nExit: %s", p->exit_label);
1925 }
1926 s = p->exit_label + p->exit_label_size;
1927 if (s[0] && s[1]) {
1928 gbfprintf(fd, "\n%s", s + 1);
1929 }
1930 if (fd->memlen) {
1931 gbfputc(0, fd);
1932 #if NEW_STRINGS
1933 // Reconsider if there's a less grubby way to do this.
1934 wp->notes = QString::fromUtf8((const char*) fd->handle.mem, fd->memlen);
1935 #else
1936 wp->notes = (char*) xmalloc(fd->memlen);
1937 memcpy(wp->notes, fd->handle.mem, fd->memlen);
1938 #endif
1939 }
1940 gbfclose(fd);
1941 return wp;
1942 }
1943
1944 static void
read_route(route_head * route)1945 read_route(route_head* route)
1946 {
1947 message_t m;
1948 message_t* msg_array;
1949 const msg_route_header_t* p;
1950 unsigned msg_array_n;
1951 unsigned wp_array_i = 0;
1952 unsigned route_total, shape_total, total;
1953 unsigned i;
1954 int attempt = ATTEMPT_MAX;
1955
1956 message_init(&m);
1957 for (;;) {
1958 m.size = MSG_REQUEST_ROUTES_SIZE;
1959 memset(m.data, 0, m.size);
1960 ((char*)m.data)[0] = 1; // Download single route
1961 strcpy((char*)m.data + 1, CSTRc(route->rte_name));
1962 message_write(MSG_REQUEST_ROUTES, &m);
1963 if (get_batch(&msg_array, &msg_array_n)) {
1964 break;
1965 }
1966 if (--attempt == 0) {
1967 fatal(MYNAME ": reading route '%s' failed (timed out)\n", qPrintable(route->rte_name));
1968 }
1969 if (global_opts.debug_level >= DBGLVL_M) {
1970 warning(MYNAME ": timed out reading route route '%s', retrying\n", qPrintable(route->rte_name));
1971 }
1972 m.size = MSG_BREAK_SIZE;
1973 memset(m.data, 0, m.size);
1974 message_write(MSG_BREAK, &m);
1975 }
1976 message_free(&m);
1977 if (msg_array_n == 0 || message_get_id(&msg_array[0]) != MSG_ROUTE_HEADER_OUT) {
1978 fatal(MYNAME ": missing route header\n");
1979 }
1980 p = (const msg_route_header_t*) msg_array[0].data;
1981 route_total = le_readu32(p->total_route_point);
1982 shape_total = le_readu32(p->total_shape_point);
1983 total = route_total + shape_total;
1984 wp_array = (Waypoint**) xcalloc(total, sizeof(*wp_array));
1985 if (global_opts.debug_level >= DBGLVL_L) {
1986 warning(MYNAME ": route '%s' %u points, %u shape points\n",
1987 qPrintable(route->rte_name), route_total, shape_total);
1988 }
1989 message_free(&msg_array[0]);
1990 for (i = 1; i < msg_array_n; i++) {
1991 unsigned id = message_get_id(&msg_array[i]);
1992 if (id == MSG_ROUTE_POINT_OUT) {
1993 wp_array[wp_array_i] = decode_route_point(msg_array[i].data);
1994 if (global_opts.debug_level >= DBGLVL_L) {
1995 warning(MYNAME ": route point '%s'\n", qPrintable(wp_array[wp_array_i]->shortname));
1996 }
1997 wp_array_i++;
1998 } else if (id == MSG_ROUTE_SHAPE_OUT) {
1999 decode_route_shape(msg_array[i].data, &wp_array_i);
2000 } else {
2001 fatal(MYNAME ": unexpected message %x while reading route '%s'\n", id, qPrintable(route->rte_name));
2002 }
2003 message_free(&msg_array[i]);
2004 }
2005 xfree(msg_array);
2006 if (total != wp_array_i) {
2007 fatal(MYNAME ": route point count mismatch, expected %u, got %u\n", total, wp_array_i);
2008 }
2009 for (i = 0; i < total; i++) {
2010 route_add_wpt(route, wp_array[i]);
2011 }
2012 xfree(wp_array);
2013 route_add_head(route);
2014 }
2015
2016 static void
read_routes(void)2017 read_routes(void)
2018 {
2019 message_t m;
2020 message_t* msg_array;
2021 unsigned msg_array_n;
2022 route_head** route_array;
2023 unsigned total;
2024 unsigned i;
2025 int attempt = ATTEMPT_MAX;
2026
2027 message_init(&m);
2028 // get number of routes
2029 for (;;) {
2030 m.size = 0;
2031 message_write(MSG_ROUTE_COUNT, &m);
2032 if (message_read(MSG_ROUTE_COUNT, &m)) {
2033 break;
2034 }
2035 if (--attempt == 0) {
2036 fatal(MYNAME ": reading route count failed\n");
2037 }
2038 }
2039 total = le_readu32(m.data);
2040 if (global_opts.debug_level >= DBGLVL_L) {
2041 warning(MYNAME ": %u routes\n", total);
2042 }
2043 if (total == 0) {
2044 message_free(&m);
2045 return;
2046 }
2047
2048 // First get route headers, then request each route
2049 attempt = ATTEMPT_MAX;
2050 for (;;) {
2051 m.size = MSG_REQUEST_ROUTES_SIZE;
2052 memset(m.data, 0, m.size);
2053 ((char*)m.data)[0] = 2; // Download all route headers
2054 message_write(MSG_REQUEST_ROUTES, &m);
2055 if (get_batch(&msg_array, &msg_array_n)) {
2056 break;
2057 }
2058 if (--attempt == 0) {
2059 fatal(MYNAME ": reading route headers failed\n");
2060 }
2061 if (global_opts.debug_level >= DBGLVL_M) {
2062 warning(MYNAME ": timed out reading route headers, retrying\n");
2063 }
2064 m.size = MSG_BREAK_SIZE;
2065 memset(m.data, 0, m.size);
2066 message_write(MSG_BREAK, &m);
2067 }
2068 message_free(&m);
2069 route_array = (route_head**) xcalloc(total, sizeof(*route_array));
2070 for (i = 0; i < msg_array_n; i++) {
2071 unsigned id = message_get_id(&msg_array[i]);
2072 if (id == MSG_ROUTE_HEADER_OUT) {
2073 route_array[i] = route_head_alloc();
2074 route_array[i]->rte_name = ((msg_route_header_t*)msg_array[i].data)->name;
2075 } else {
2076 fatal(MYNAME ": unexpected message %x while reading route headers\n", id);
2077 }
2078 message_free(&msg_array[i]);
2079 }
2080 xfree(msg_array);
2081 // get each route
2082 for (i = 0; i < total; i++) {
2083 read_route(route_array[i]);
2084 }
2085 xfree(route_array);
2086 }
2087
2088 //-----------------------------------------------------------------------------
2089 // Route writing
2090
2091 static unsigned route_point_n;
2092 static unsigned shape_point_n;
2093 static unsigned* shape_point_counts;
2094
2095 static void
write_route_shape_points(Waypoint ** array,unsigned n)2096 write_route_shape_points(Waypoint** array, unsigned n)
2097 {
2098 message_t m;
2099 const unsigned pt_per_msg = 25;
2100 msg_route_shape_t* p = NULL;
2101 unsigned i = 0;
2102 unsigned j = 0;
2103
2104 do {
2105 if (j == 0) {
2106 message_init_size(&m, 10 + 8 * pt_per_msg);
2107 p = (msg_route_shape_t*) m.data;
2108 le_write32(p->total, n);
2109 le_write32(p->index, i + 1);
2110 p->reserved = 0;
2111 }
2112 assert(p);
2113 le_write32(p->point[j].latitude, delbin_deg2rad(array[i]->latitude));
2114 le_write32(p->point[j].longitude, delbin_deg2rad(array[i]->longitude));
2115 i++;
2116 j++;
2117 if (j == pt_per_msg || i == n) {
2118 p->number = j;
2119 m.size = 10 + 8 * j;
2120 add_to_batch(MSG_ROUTE_SHAPE_IN, &m);
2121 j = 0;
2122 }
2123 } while (i < n);
2124 }
2125
2126 static void
write_route_points(void)2127 write_route_points(void)
2128 {
2129 unsigned route_point_i = 0;
2130 unsigned i = 0;
2131
2132 while (i < waypoint_n) {
2133 message_t m;
2134 unsigned shape_n;
2135 const Waypoint* wp = wp_array[i];
2136 msg_route_point_t* p;
2137 char* s;
2138
2139 message_init_size(&m, sizeof(msg_route_point_t) + 1 + 1 + 4);
2140 p = (msg_route_point_t*) m.data;
2141 memset(m.data, 0, m.size);
2142 route_point_i++;
2143 shape_n = shape_point_counts[route_point_i];
2144 le_write32(p->total, route_point_n);
2145 le_write32(p->index, route_point_i);
2146 if (!wp->shortname.isEmpty()) {
2147 strncpy(p->name, CSTRc(wp->shortname), sizeof(p->name) - 1);
2148 } else {
2149 sprintf(p->name, "RPT%u", route_point_i);
2150 }
2151 le_write32(p->latitude, delbin_deg2rad(wp->latitude));
2152 le_write32(p->longitude, delbin_deg2rad(wp->longitude));
2153 p->exit_label_size = 1;
2154 s = p->exit_label + p->exit_label_size;
2155 s[0] = 1; // comment size
2156 le_write32(s + 2, shape_n);
2157 if (route_point_i == 1) {
2158 p->itinerary_type = 1; // start
2159 } else if (route_point_i == route_point_n) {
2160 p->itinerary_type = 3; // finish
2161 }
2162 add_to_batch(MSG_ROUTE_POINT_IN, &m);
2163 i++;
2164 if (shape_n) {
2165 write_route_shape_points(&wp_array[i], shape_n);
2166 i += shape_n;
2167 }
2168 }
2169 }
2170
2171 static void
write_route_begin(const route_head * track)2172 write_route_begin(const route_head* track)
2173 {
2174 waypoint_i = 0;
2175 route_point_n = 0;
2176 shape_point_n = 0;
2177 waypoint_n = track->rte_waypt_ct;
2178 if (waypoint_n) {
2179 wp_array = (Waypoint**) xmalloc(waypoint_n * sizeof(*wp_array));
2180 shape_point_counts = (unsigned int*) xcalloc(waypoint_n, sizeof(*shape_point_counts));
2181 }
2182 }
2183
2184 static void
write_route_point(const Waypoint * wp)2185 write_route_point(const Waypoint* wp)
2186 {
2187 wp_array[waypoint_i++] = (Waypoint*)wp;
2188 if (wp->shortname.startsWith("SHP")) {
2189 shape_point_n++;
2190 shape_point_counts[route_point_n]++;
2191 } else {
2192 route_point_n++;
2193 }
2194 }
2195
2196 static void
write_route_end(const route_head * route)2197 write_route_end(const route_head* route)
2198 {
2199 message_t m;
2200 msg_route_header_in_t* p;
2201
2202 if (waypoint_n == 0) {
2203 return;
2204 }
2205 message_init_size(&m, sizeof(msg_route_header_in_t));
2206 p = (msg_route_header_in_t*) m.data;
2207 memset(p->name, 0, sizeof(p->name));
2208 if (!route->rte_name.isEmpty()) {
2209 strncpy(p->name, CSTRc(route->rte_name), sizeof(p->name) - 1);
2210 } else {
2211 sprintf(p->name, "%lu", (long)wp_array[0]->GetCreationTime().toTime_t());
2212 }
2213 p->type = 0;
2214 le_write32(p->total_route_point, route_point_n);
2215 le_write32(p->total_shape_point, shape_point_n);
2216 add_to_batch(MSG_ROUTE_HEADER_IN, &m);
2217 write_route_points();
2218 send_batch();
2219 if (wp_array) {
2220 xfree(wp_array);
2221 xfree(shape_point_counts);
2222 }
2223 }
2224
2225 static void
write_routes(void)2226 write_routes(void)
2227 {
2228 route_disp_all(write_route_begin, write_route_end, write_route_point);
2229 }
2230
2231 //-----------------------------------------------------------------------------
2232 // Current position
2233
2234 static Waypoint*
decode_navmsg(const void * data)2235 decode_navmsg(const void* data)
2236 {
2237 Waypoint* wp = new Waypoint;
2238 const msg_navigation_t* p = (const msg_navigation_t*) data;
2239 struct tm t;
2240
2241 t.tm_year = le_readu16(p->year) - 1900;
2242 t.tm_mon = p->month - 1;
2243 t.tm_mday = p->day;
2244 t.tm_hour = p->hour;
2245 t.tm_min = p->minute;
2246 t.tm_sec = p->second;
2247 wp->SetCreationTime(mkgmtime(&t));
2248 wp->sat = p->satellites;
2249 wp->latitude = le_read_double(p->latitude);
2250 wp->longitude = le_read_double(p->longitude);
2251 wp->altitude = le_read_double(p->elevation);
2252 wp->speed = le_read_float(p->speed);
2253 wp->speed *= (1000.0f / (60 * 60));
2254 wp->wpt_flags.speed = 1;
2255 wp->course = le_readu16(p->heading);
2256 wp->course /= 100;
2257 wp->wpt_flags.course = 1;
2258 decode_sat_fix(wp, p->fix_status);
2259 wp->shortname = "Position";
2260 return wp;
2261 }
2262
2263 static Waypoint*
read_position(void)2264 read_position(void)
2265 {
2266 Waypoint* wp;
2267 message_t m;
2268
2269 message_init(&m);
2270 message_read(MSG_NAVIGATION, &m);
2271 wp = decode_navmsg(m.data);
2272 if (wp->fix > fix_none &&
2273 message_read_1(MSG_SATELLITE_INFO, &m) == MSG_SATELLITE_INFO) {
2274 const msg_satellite_t* p = (const msg_satellite_t*) m.data;
2275 wp->hdop = le_readu16(p->hdop);
2276 wp->hdop /= 100;
2277 wp->vdop = le_readu16(p->vdop);
2278 wp->vdop /= 100;
2279 wp->pdop = le_readu16(p->pdop);
2280 wp->pdop /= 100;
2281 }
2282 message_free(&m);
2283 return wp;
2284 }
2285
2286 //-----------------------------------------------------------------------------
2287
2288 static void
delbin_list_units()2289 delbin_list_units()
2290 {
2291 int i;
2292 for (i = 0; i < n_delbin_units; i++) {
2293 printf("%u %s %s\n",
2294 delbin_unit_info[i].unit_number,
2295 delbin_unit_info[i].unit_serial_number,
2296 delbin_unit_info[i].unit_name);
2297 }
2298 }
2299
2300 static void
delbin_rw_init(const QString & fname)2301 delbin_rw_init(const QString& fname)
2302 {
2303 message_t m;
2304 char buf[256];
2305
2306 if (!mkshort_handle) {
2307 mkshort_handle = mkshort_new_handle();
2308 }
2309 // Contrary to doc, it looks like there's a limit of 32 bytes
2310 // and a null terminator is required, at least in F/W 2.6.210726
2311 // on a PN-40.
2312 setshort_length(mkshort_handle, 31);
2313 setshort_whitespace_ok(mkshort_handle, 1);
2314 setshort_badchars(mkshort_handle, "");
2315 setshort_mustuniq(mkshort_handle, 1);
2316
2317 delbin_os_ops.init(fname);
2318
2319 // Often the first packet is part of an old message, sometimes it can
2320 // confuse the first message read if we don't get rid of it
2321 packet_read(buf);
2322 // Send a break to clear any state from a previous failure
2323 message_init_size(&m, MSG_BREAK_SIZE);
2324 memset(m.data, 0, m.size);
2325 message_write(MSG_BREAK, &m);
2326 // get version info
2327 m.size = 0;
2328 message_write(MSG_VERSION, &m);
2329 if (message_read(MSG_VERSION, &m)) {
2330 const msg_version_t* p = (const msg_version_t*) m.data;
2331 if (global_opts.debug_level >= DBGLVL_L) {
2332 warning(MYNAME ": device %s %s\n", p->product, p->firmware);
2333 }
2334 if (opt_long_notes) {
2335 use_extended_notes = TRUE;
2336 } else if (strstr(p->product, "PN-20")) {
2337 use_extended_notes = p->firmware[0] > '1' ||
2338 (p->firmware[0] == '1' && p->firmware[2] >= '6');
2339 } else if (strstr(p->product, "PN-30") || strstr(p->product, "PN-40")) {
2340 use_extended_notes = p->firmware[0] > '2' ||
2341 (p->firmware[0] == '2' && p->firmware[2] >= '5');
2342 } else {
2343 // assume PN-60 or later
2344 use_extended_notes = TRUE;
2345 }
2346 delbin_unit_info[n_delbin_units].unit_number = n_delbin_units;
2347 delbin_unit_info[n_delbin_units].unit_serial_number = xstrndup(p->serial, sizeof(p->serial));
2348 delbin_unit_info[n_delbin_units].unit_name = xstrndup(p->product, sizeof(p->product));
2349 n_delbin_units++;
2350 }
2351 message_free(&m);
2352
2353 if (fname.length() > 4) {
2354 if (fname.mid(4,4) == "list") {
2355 delbin_list_units();
2356 exit(1);
2357 }
2358 }
2359 }
2360
2361 static void
delbin_rw_deinit(void)2362 delbin_rw_deinit(void)
2363 {
2364 if (mkshort_handle) {
2365 mkshort_del_handle(&mkshort_handle);
2366 }
2367 delbin_os_ops.deinit();
2368 }
2369
2370 static void
delbin_read(void)2371 delbin_read(void)
2372 {
2373 if (doing_wpts) {
2374 if (opt_getposn) {
2375 waypt_add(read_position());
2376 } else {
2377 read_waypoints();
2378 }
2379 }
2380 if (doing_trks) {
2381 read_tracks();
2382 }
2383 if (doing_rtes) {
2384 read_routes();
2385 }
2386 }
2387
2388 static void
delbin_write(void)2389 delbin_write(void)
2390 {
2391 if (doing_wpts) {
2392 message_t m;
2393 device_max_waypoint = 1000;
2394 message_init_size(&m, 0);
2395 message_write(MSG_CAPABILITIES, &m);
2396 if (message_read(MSG_CAPABILITIES, &m)) {
2397 const msg_capabilities_t* p = (const msg_capabilities_t*) m.data;
2398 device_max_waypoint = le_readu32(p->max_waypoints);
2399 }
2400 message_free(&m);
2401
2402 if (opt_nuke_wpt) {
2403 add_nuke(nuke_type_wpt);
2404 }
2405 write_waypoints();
2406 }
2407 if (doing_trks) {
2408 if (opt_nuke_trk) {
2409 add_nuke(nuke_type_trk);
2410 }
2411 write_tracks();
2412 }
2413 if (doing_rtes) {
2414 if (opt_nuke_rte) {
2415 add_nuke(nuke_type_rte);
2416 }
2417 write_routes();
2418 }
2419 }
2420
2421 static Waypoint*
delbin_rd_position(posn_status * status)2422 delbin_rd_position(posn_status* status)
2423 {
2424 return read_position();
2425 }
2426
2427 ff_vecs_t delbin_vecs = {
2428 ff_type_serial,
2429 FF_CAP_RW_ALL,
2430 delbin_rw_init,
2431 delbin_rw_init,
2432 delbin_rw_deinit,
2433 delbin_rw_deinit,
2434 delbin_read,
2435 delbin_write,
2436 NULL,
2437 delbin_args,
2438 CET_CHARSET_LATIN1, 1,
2439 { delbin_rw_init, delbin_rd_position, delbin_rw_deinit }
2440 };
2441
2442 //=============================================================================
2443 // OS device I/O implementations
2444
2445 #define VENDOR_ID 0x1163
2446 #define PRODUCT_ID 0x2020
2447
2448 //-----------------------------------------------------------------------------
2449 // Windows
2450 #ifdef HAVE_WDK
2451
2452 #undef HAVE_LIBUSB
2453
2454 #define WIN32_LEAN_AND_MEAN
2455 #include <windows.h>
2456 #include <setupapi.h>
2457 // If hidsdi.h is not found, you need to download the Windows Driver Kit,
2458 // from http://www.microsoft.com/whdc/Devtools/wdk/default.mspx
2459 // You need to install 'build environments' and 'tools' from the SDK and
2460 // follow the instructions in the Install.html to get MSVC to find the right
2461 // headers and libraries.
2462 #include <specstrings.h>
2463 #include <hidsdi.h>
2464
2465 static HANDLE hid_handle;
2466
2467 static void
win_os_init(const QString & fname)2468 win_os_init(const QString& fname)
2469 {
2470 GUID hid_guid;
2471 HDEVINFO dev_info;
2472 SP_DEVICE_INTERFACE_DATA dev_int_data;
2473 PHIDP_PREPARSED_DATA hid_ppd;
2474 HIDP_CAPS hid_caps;
2475 const char* busy = "";
2476 unsigned i;
2477
2478 hid_handle = INVALID_HANDLE_VALUE;
2479 HidD_GetHidGuid(&hid_guid);
2480 dev_info = SetupDiGetClassDevs(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
2481 if (dev_info == INVALID_HANDLE_VALUE) {
2482 fatal(MYNAME ": SetupDiGetClassDevs failed %u\n", GetLastError());
2483 }
2484 dev_int_data.cbSize = sizeof(dev_int_data);
2485 for (i = 0; SetupDiEnumDeviceInterfaces(dev_info, NULL, &hid_guid, i, &dev_int_data); i++) {
2486 union {
2487 SP_DEVICE_INTERFACE_DETAIL_DATA detail_data;
2488 char buf[300];
2489 } u;
2490 u.detail_data.cbSize = sizeof(u.detail_data);
2491 if (SetupDiGetDeviceInterfaceDetail(dev_info,
2492 &dev_int_data, &u.detail_data, sizeof(u.buf), NULL, NULL)) {
2493 HANDLE h = CreateFile(u.detail_data.DevicePath,
2494 FILE_READ_DATA | FILE_WRITE_DATA, 0, NULL, OPEN_EXISTING, 0, NULL);
2495 if (h != INVALID_HANDLE_VALUE) {
2496 HIDD_ATTRIBUTES hid_attr;
2497 hid_attr.Size = sizeof(hid_attr);
2498 if (HidD_GetAttributes(h, &hid_attr) &&
2499 hid_attr.VendorID == VENDOR_ID && hid_attr.ProductID == PRODUCT_ID) {
2500 hid_handle = h;
2501 break;
2502 }
2503 CloseHandle(h);
2504 } else if (GetLastError() == ERROR_SHARING_VIOLATION &&
2505 strstr(u.detail_data.DevicePath, "1163") &&
2506 strstr(u.detail_data.DevicePath, "2020")) {
2507 busy = " (device busy?)";
2508 }
2509 }
2510 }
2511 SetupDiDestroyDeviceInfoList(dev_info);
2512 if (hid_handle == INVALID_HANDLE_VALUE) {
2513 fatal(MYNAME ": no DeLorme PN found%s\n", busy);
2514 }
2515 if (!HidD_GetPreparsedData(hid_handle, &hid_ppd)) {
2516 fatal(MYNAME ": HidD_GetPreparsedData failed %u\n", GetLastError());
2517 }
2518 if (!HidP_GetCaps(hid_ppd, &hid_caps)) {
2519 fatal(MYNAME ": HidP_GetCaps failed %u\n", GetLastError());
2520 }
2521 // report length includes report id
2522 delbin_os_packet_size = hid_caps.InputReportByteLength - 1;
2523 HidD_FreePreparsedData(hid_ppd);
2524 }
2525
2526 static void
win_os_deinit(void)2527 win_os_deinit(void)
2528 {
2529 CloseHandle(hid_handle);
2530 }
2531
2532 static unsigned
win_os_packet_read(void * buf)2533 win_os_packet_read(void* buf)
2534 {
2535 DWORD n;
2536 char buf1[257];
2537 // first byte is report id
2538 if (ReadFile(hid_handle, buf1, delbin_os_packet_size + 1, &n, NULL) == 0) {
2539 unsigned err = GetLastError();
2540 fatal(MYNAME ": ReadFile failed %u\n", err);
2541 }
2542 if (n > 0) {
2543 n--;
2544 }
2545 memcpy(buf, buf1 + 1, n);
2546 return n;
2547 }
2548
2549 static unsigned
win_os_packet_write(const void * buf,unsigned size)2550 win_os_packet_write(const void* buf, unsigned size)
2551 {
2552 DWORD n;
2553 char buf1[257];
2554 // first byte is report id
2555 buf1[0] = 0;
2556 memcpy(buf1 + 1, buf, size);
2557 if (WriteFile(hid_handle, buf1, delbin_os_packet_size + 1, &n, NULL) == 0) {
2558 unsigned err = GetLastError();
2559 fatal(MYNAME ": WriteFile of %u bytes failed with %u. Size: %u Wrote: %d\n",
2560 delbin_os_packet_size + 1, err, size, (int) n);
2561 }
2562 if (n > size) {
2563 n = size;
2564 }
2565 return n;
2566 }
2567
2568 delbin_os_ops_t delbin_os_ops = {
2569 win_os_init,
2570 win_os_deinit,
2571 win_os_packet_read,
2572 win_os_packet_write
2573 };
2574
2575 #endif // HAVE_WDK
2576
2577 //-----------------------------------------------------------------------------
2578 // MacOS X
2579 #if __APPLE__
2580
2581 #undef HAVE_LIBUSB
2582
2583 #include <pthread.h>
2584 #include <IOKit/IOKitLib.h>
2585 #include <IOKit/IOCFPlugIn.h>
2586 #include <IOKit/hid/IOHIDLib.h>
2587 #include <mach/mach_error.h>
2588
2589 // IOHIDDeviceInterface121::getReport() does not work, it hangs the process
2590 // in some sort of unkillable state. So reading is done via a separate thread
2591 // with a run loop and interrupt report callback. Yuck.
2592
2593 static IOHIDDeviceInterface122** device;
2594 static pthread_t thread;
2595 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
2596 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
2597 static char* report_buf;
2598 static char* packet_array[32];
2599 static unsigned packet_array_head;
2600 static unsigned packet_array_tail;
2601 static CFRunLoopRef run_loop;
2602
2603 static void*
thread_func(void * run_loop_source)2604 thread_func(void* run_loop_source)
2605 {
2606 run_loop = CFRunLoopGetCurrent();
2607 #if __cplusplus
2608 CFRunLoopAddSource(run_loop, (__CFRunLoopSource*) run_loop_source, kCFRunLoopDefaultMode);
2609 #else
2610 CFRunLoopAddSource(run_loop, run_loop_source, kCFRunLoopDefaultMode);
2611 #endif
2612 CFRunLoopRun();
2613 return NULL;
2614 }
2615
2616 static void
interrupt_report_cb(void * target,IOReturn result,void * refcon,void * sender,UInt32 bufferSize)2617 interrupt_report_cb(void* target, IOReturn result, void* refcon, void* sender, UInt32 bufferSize)
2618 {
2619 memcpy(packet_array[packet_array_head], report_buf, delbin_os_packet_size);
2620 pthread_mutex_lock(&mutex);
2621 if (packet_array_head == packet_array_tail) {
2622 pthread_cond_signal(&cond);
2623 }
2624 packet_array_head++;
2625 packet_array_head &= sizeofarray(packet_array) - 1;
2626 if (packet_array_head == packet_array_tail && global_opts.debug_level >= DBGLVL_M) {
2627 warning(MYNAME ": packet_array overrun, packets lost\n");
2628 }
2629 pthread_mutex_unlock(&mutex);
2630 }
2631
2632 static void
mac_os_init(const QString & fname)2633 mac_os_init(const QString& fname)
2634 {
2635 CFMutableDictionaryRef dict = IOServiceMatching(kIOHIDDeviceKey);
2636 io_service_t service;
2637 IOCFPlugInInterface** plugin;
2638 CFNumberRef cf_num;
2639 CFRunLoopSourceRef run_loop_source;
2640 int i;
2641 kern_return_t kr;
2642 HRESULT hr;
2643 IOReturn ir;
2644 SInt32 unused;
2645
2646 i = VENDOR_ID;
2647 cf_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &i);
2648 CFDictionaryAddValue(dict, CFSTR(kIOHIDVendorIDKey), cf_num);
2649 CFRelease(cf_num);
2650 i = PRODUCT_ID;
2651 cf_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &i);
2652 CFDictionaryAddValue(dict, CFSTR(kIOHIDProductIDKey), cf_num);
2653 CFRelease(cf_num);
2654 service = IOServiceGetMatchingService(kIOMasterPortDefault, dict);
2655 if (service == 0) {
2656 fatal(MYNAME ": no DeLorme PN found\n");
2657 }
2658 kr = IOCreatePlugInInterfaceForService(
2659 service, kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &unused);
2660 if (kr) {
2661 fatal(MYNAME ": IOCreatePlugInInterfaceForService failed 0x%x\n", (int)kr);
2662 }
2663 IOObjectRelease(service);
2664 hr = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID122), (void**)&device);
2665 if (hr) {
2666 fatal(MYNAME ": QueryInterface failed 0x%x\n", (int)hr);
2667 }
2668 (*plugin)->Release(plugin);
2669 ir = (*device)->open(device, kIOHIDOptionsTypeSeizeDevice);
2670 if (ir)
2671 fatal(MYNAME ": device open failed 0x%x - %s\n", (int)ir,
2672 mach_error_string(ir));
2673 ir = (*device)->createAsyncEventSource(device, &run_loop_source);
2674 if (ir) {
2675 fatal(MYNAME ": createAsyncEventSource failed 0x%x\n", (int)ir);
2676 }
2677 delbin_os_packet_size = 64;
2678 report_buf = (char*)xmalloc(delbin_os_packet_size);
2679 for (i = sizeofarray(packet_array); i--;) {
2680 packet_array[i] = (char*)xmalloc(delbin_os_packet_size);
2681 }
2682 ir = (*device)->setInterruptReportHandlerCallback(
2683 device, report_buf, delbin_os_packet_size, interrupt_report_cb, NULL, NULL);
2684 if (ir) {
2685 fatal(MYNAME ": setInterruptReportHandlerCallback failed 0x%x\n", (int)ir);
2686 }
2687 i = pthread_create(&thread, NULL, thread_func, run_loop_source);
2688 if (i) {
2689 fatal(MYNAME ": pthread_create failed %d\n", i);
2690 }
2691 }
2692
2693 static void
mac_os_deinit(void)2694 mac_os_deinit(void)
2695 {
2696 void* unused;
2697 unsigned i;
2698 CFRunLoopStop(run_loop);
2699 pthread_join(thread, &unused);
2700 (*device)->Release(device);
2701 xfree(report_buf);
2702 for (i = sizeofarray(packet_array); i--;) {
2703 xfree(packet_array[i]);
2704 }
2705 }
2706
2707 static unsigned
mac_os_packet_read(void * buf)2708 mac_os_packet_read(void* buf)
2709 {
2710 pthread_mutex_lock(&mutex);
2711 while (packet_array_head == packet_array_tail) {
2712 pthread_cond_wait(&cond, &mutex);
2713 }
2714 memcpy(buf, packet_array[packet_array_tail++], delbin_os_packet_size);
2715 packet_array_tail &= sizeofarray(packet_array) - 1;
2716 pthread_mutex_unlock(&mutex);
2717 return delbin_os_packet_size;
2718 }
2719
2720 static unsigned
mac_os_packet_write(const void * buf,unsigned size)2721 mac_os_packet_write(const void* buf, unsigned size)
2722 {
2723 IOReturn r = (*device)->setReport(
2724 device, kIOHIDReportTypeOutput, 0, (void*)buf, size, 2000, NULL, NULL, NULL);
2725 if (r) {
2726 fatal("setReport failed 0x%x\n", (int)r);
2727 }
2728 return size;
2729 }
2730
2731 delbin_os_ops_t delbin_os_ops = {
2732 mac_os_init,
2733 mac_os_deinit,
2734 mac_os_packet_read,
2735 mac_os_packet_write
2736 };
2737
2738 #endif // __APPLE__
2739
2740 //-----------------------------------------------------------------------------
2741 // libusb
2742 #if HAVE_LIBUSB
2743
2744 #include <usb.h>
2745
2746 static struct usb_device* usb_dev;
2747 static usb_dev_handle* usb_handle;
2748 static int endpoint_in;
2749 static int endpoint_out;
2750
2751 static void
libusb_os_init(const QString & fname)2752 libusb_os_init(const QString& fname)
2753 {
2754 struct usb_bus* bus;
2755 const struct usb_endpoint_descriptor* endpoint_desc;
2756
2757 usb_init();
2758 usb_find_busses();
2759 usb_find_devices();
2760 for (bus = usb_busses; usb_dev == NULL && bus; bus = bus->next) {
2761 struct usb_device* d;
2762 for (d = bus->devices; d; d = d->next) {
2763 if (d->descriptor.idVendor == VENDOR_ID && d->descriptor.idProduct == PRODUCT_ID) {
2764 usb_dev = d;
2765 break;
2766 }
2767 }
2768 }
2769 if (usb_dev == NULL) {
2770 fatal(MYNAME ": no DeLorme PN found\n");
2771 }
2772 usb_handle = usb_open(usb_dev);
2773 if (usb_handle == NULL) {
2774 fatal(MYNAME ": %s\n", usb_strerror());
2775 }
2776
2777 // Device has 1 configuration, 1 interface, 2 interrupt endpoints
2778 if (usb_claim_interface(usb_handle, 0) < 0) {
2779 #if LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
2780 if (usb_detach_kernel_driver_np(usb_handle, 0) < 0) {
2781 warning(MYNAME ": %s\n", usb_strerror());
2782 }
2783 if (usb_claim_interface(usb_handle, 0) < 0)
2784 #endif
2785 {
2786 const char* s = usb_strerror();
2787 usb_close(usb_handle);
2788 fatal(MYNAME ": %s\n", s);
2789 }
2790 }
2791 endpoint_desc = usb_dev->config[0].interface[0].altsetting[0].endpoint;
2792 delbin_os_packet_size = endpoint_desc[0].wMaxPacketSize;
2793 endpoint_in = endpoint_desc[0].bEndpointAddress;
2794 endpoint_out = endpoint_desc[1].bEndpointAddress;
2795 if ((endpoint_in & USB_ENDPOINT_DIR_MASK) == USB_ENDPOINT_OUT) {
2796 int t = endpoint_in;
2797 endpoint_in = endpoint_out;
2798 endpoint_out = t;
2799 }
2800 }
2801
2802 static void
libusb_os_deinit(void)2803 libusb_os_deinit(void)
2804 {
2805 usb_release_interface(usb_handle, 0);
2806 usb_close(usb_handle);
2807 }
2808
2809 static unsigned
libusb_os_packet_read(void * buf)2810 libusb_os_packet_read(void* buf)
2811 {
2812 int n = usb_interrupt_read(usb_handle, endpoint_in, (char*) buf, delbin_os_packet_size, 2000);
2813 if (n < 0) {
2814 fatal(MYNAME ": %s\n", usb_strerror());
2815 }
2816 return n;
2817 }
2818
2819 static unsigned
libusb_os_packet_write(const void * buf,unsigned size)2820 libusb_os_packet_write(const void* buf, unsigned size)
2821 {
2822 int n = usb_interrupt_write(usb_handle, endpoint_out, (char*)buf, size, 2000);
2823 if (n < 0) {
2824 fatal(MYNAME ": %s\n", usb_strerror());
2825 }
2826 return n;
2827 }
2828
2829 #if HAVE_LINUX_HID
2830 static const delbin_os_ops_t libusb_os_ops =
2831 #else
2832 delbin_os_ops_t delbin_os_ops =
2833 #endif
2834 {
2835 libusb_os_init,
2836 libusb_os_deinit,
2837 libusb_os_packet_read,
2838 libusb_os_packet_write
2839 };
2840
2841 #endif // HAVE_LIBUSB
2842
2843 //-----------------------------------------------------------------------------
2844 // Linux
2845 #if HAVE_LINUX_HID
2846
2847 #include <unistd.h>
2848 #include <fcntl.h>
2849 #include <dirent.h>
2850 #include <errno.h>
2851 #include <sys/ioctl.h>
2852 #include <linux/types.h>
2853 #include <linux/hiddev.h>
2854 #include <linux/hidraw.h>
2855
2856 // Read from hidraw, write to hiddev. Reading from hiddev does not work,
2857 // and neither does writing to hidraw.
2858 static int fd_hidraw;
2859 static int fd_hiddev;
2860
2861 static int linuxhid_os_init_status;
2862
2863 static void
linuxhid_os_init(const QString & fname)2864 linuxhid_os_init(const QString& fname)
2865 {
2866 struct hidraw_devinfo info;
2867 struct hiddev_field_info finfo;
2868 DIR* dir = NULL;
2869 struct dirent* d;
2870
2871 fd_hidraw = fd_hiddev = -1;
2872 if (fname.startsWith("hid:")) {
2873 char* raw_name = xstrdup(qPrintable(fname.mid(4)));
2874 char* dev_name = strchr(raw_name, ',');
2875 if (dev_name == NULL) {
2876 fatal(MYNAME ": missing hiddev path\n");
2877 }
2878 *dev_name++ = 0;
2879 fd_hidraw = open(raw_name, O_RDONLY);
2880 if (fd_hidraw < 0) {
2881 fatal(MYNAME ": %s: %s\n", raw_name, strerror(errno));
2882 }
2883 fd_hiddev = open(dev_name, O_WRONLY);
2884 if (fd_hiddev < 0) {
2885 fatal(MYNAME ": %s: %s\n", dev_name, strerror(errno));
2886 }
2887 xfree(raw_name);
2888 } else {
2889 dir = opendir("/dev");
2890 }
2891 while (dir && (d = readdir(dir)) != NULL) {
2892 if (strncmp(d->d_name, "hidraw", 6) == 0) {
2893 int fd1, fd2;
2894 char raw_name[32];
2895 char dev_name[32];
2896 sprintf(raw_name, "/dev/%s", d->d_name);
2897 fd1 = open(raw_name, O_RDONLY);
2898 if (fd1 < 0) {
2899 if (global_opts.debug_level >= DBGLVL_M) {
2900 warning(MYNAME ": %s: %s\n", raw_name, strerror(errno));
2901 }
2902 continue;
2903 }
2904 sprintf(dev_name, "/dev/usb/hiddev%s", raw_name + sizeof("/dev/hidraw") - 1);
2905 fd2 = open(dev_name, O_WRONLY);
2906 if (fd2 < 0 && errno == ENOENT) {
2907 sprintf(dev_name, "/dev/hiddev%s", raw_name + sizeof("/dev/hidraw") - 1);
2908 fd2 = open(dev_name, O_WRONLY);
2909 }
2910 if (fd2 < 0) {
2911 if (global_opts.debug_level >= DBGLVL_M) {
2912 warning(MYNAME ": %s: %s\n", dev_name, strerror(errno));
2913 }
2914 close(fd1);
2915 continue;
2916 }
2917 if (ioctl(fd1, HIDIOCGRAWINFO, &info) == 0 &&
2918 info.vendor == VENDOR_ID && info.product == PRODUCT_ID) {
2919 fd_hidraw = fd1;
2920 fd_hiddev = fd2;
2921 break;
2922 }
2923 close(fd1);
2924 close(fd2);
2925 }
2926 }
2927 if (dir) {
2928 closedir(dir);
2929 }
2930 if (fd_hidraw < 0) {
2931 if (linuxhid_os_init_status == 0) {
2932 fatal(MYNAME ": no DeLorme PN found\n");
2933 }
2934 return;
2935 }
2936 finfo.report_type = HID_REPORT_TYPE_INPUT;
2937 finfo.report_id = 0;
2938 finfo.field_index = 0;
2939 if (ioctl(fd_hiddev, HIDIOCGFIELDINFO, &finfo) < 0) {
2940 warning(MYNAME ": HIDIOCGFIELDINFO: %s\n", strerror(errno));
2941 if (linuxhid_os_init_status == 0) {
2942 exit(1);
2943 }
2944 return;
2945 }
2946 delbin_os_packet_size = finfo.maxusage;
2947 linuxhid_os_init_status = 0;
2948 }
2949
2950 static void
linuxhid_os_deinit(void)2951 linuxhid_os_deinit(void)
2952 {
2953 close(fd_hidraw);
2954 close(fd_hiddev);
2955 }
2956
2957 static unsigned
linuxhid_os_packet_read(void * buf)2958 linuxhid_os_packet_read(void* buf)
2959 {
2960 int n = read(fd_hidraw, buf, delbin_os_packet_size);
2961 if (n < 0) {
2962 fatal(MYNAME ": %s\n", strerror(errno));
2963 }
2964 return n;
2965 }
2966
2967 static unsigned
linuxhid_os_packet_write(const void * buf,unsigned size)2968 linuxhid_os_packet_write(const void* buf, unsigned size)
2969 {
2970 struct hiddev_usage_ref_multi urefm;
2971 struct hiddev_report_info rinfo;
2972 const uint8_t* p = (const uint8_t*) buf;
2973 unsigned i;
2974
2975 for (i = 0; i < size; i++) {
2976 urefm.values[i] = p[i];
2977 }
2978 urefm.num_values = size;
2979 memset(&urefm.uref, 0, sizeof(urefm.uref));
2980 urefm.uref.report_type = HID_REPORT_TYPE_OUTPUT;
2981 if (ioctl(fd_hiddev, HIDIOCSUSAGES, &urefm)) {
2982 fatal(MYNAME ": HIDIOCSUSAGES: %s\n", strerror(errno));
2983 }
2984 memset(&rinfo, 0, sizeof(rinfo));
2985 rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
2986 if (ioctl(fd_hiddev, HIDIOCSREPORT, &rinfo)) {
2987 fatal(MYNAME ": HIDIOCSREPORT: %s\n", strerror(errno));
2988 }
2989 return size;
2990 }
2991
2992 static const delbin_os_ops_t linuxhid_os_ops = {
2993 linuxhid_os_init,
2994 linuxhid_os_deinit,
2995 linuxhid_os_packet_read,
2996 linuxhid_os_packet_write
2997 };
2998
2999 static void
linux_os_init(const QString & fname)3000 linux_os_init(const QString& fname)
3001 {
3002 // tell linuxhid_os_init not to exit
3003 linuxhid_os_init_status = 1;
3004 linuxhid_os_init(fname);
3005 if (linuxhid_os_init_status == 0) {
3006 delbin_os_ops = linuxhid_os_ops;
3007 } else {
3008 #if HAVE_LIBUSB
3009 if (global_opts.debug_level >= DBGLVL_M) {
3010 warning(MYNAME ": HID init failed, falling back to libusb\n");
3011 }
3012 delbin_os_ops = libusb_os_ops;
3013 delbin_os_ops.init(fname);
3014 #else
3015 fatal(MYNAME ": no DeLorme PN found\n");
3016 #endif
3017 }
3018 }
3019
3020 delbin_os_ops_t delbin_os_ops = {
3021 linux_os_init,
3022 NULL,
3023 NULL,
3024 NULL
3025 };
3026
3027 #endif // HAVE_LINUX_HID
3028
3029 //-----------------------------------------------------------------------------
3030 // stubs
3031 #if !(HAVE_WDK || __APPLE__ || HAVE_LIBUSB || HAVE_LINUX_HID)
3032 static void
stub_os_init(const QString & fname)3033 stub_os_init(const QString& fname)
3034 {
3035 fatal(MYNAME ": OS not supported\n");
3036 }
3037 static void
stub_os_deinit(void)3038 stub_os_deinit(void)
3039 {
3040 }
3041 static unsigned
stub_os_packet_read(void * buf)3042 stub_os_packet_read(void* buf)
3043 {
3044 return 0;
3045 }
3046 static unsigned
stub_os_packet_write(const void * buf,unsigned size)3047 stub_os_packet_write(const void* buf, unsigned size)
3048 {
3049 return 0;
3050 }
3051 delbin_os_ops_t delbin_os_ops = {
3052 stub_os_init,
3053 stub_os_deinit,
3054 stub_os_packet_read,
3055 stub_os_packet_write
3056 };
3057 #endif
3058 // end OS device I/O implementations section
3059 //=============================================================================
3060
3061 static const int track_color_bgr[] = {
3062 0x0000ff, // red
3063 0x00ffff, // yellow
3064 0x008000, // green
3065 0xff0000, // blue
3066 0x808080, // gray
3067 0xffffff, // white
3068 0, // black
3069 0xffff00, // cyan
3070 0xff00ff, // magenta
3071 0x00a5ff, // orange
3072 0x82004b, // indigo
3073 0xeea5ee // violet
3074 };
3075
track_color(unsigned i)3076 static int track_color(unsigned i)
3077 {
3078 int bgr = -1;
3079 if (i < sizeofarray(track_color_bgr)) {
3080 bgr = track_color_bgr[i];
3081 }
3082 return bgr;
3083 }
3084
track_color_index(int bgr)3085 static unsigned track_color_index(int bgr)
3086 {
3087 unsigned i = sizeofarray(track_color_bgr);
3088 do {
3089 i--;
3090 } while (i != 0 && track_color_bgr[i] != bgr);
3091 return i;
3092 }
3093
3094 static const char* const waypoint_symbol_name[] = {
3095 // 0
3096 "Red Map Pin",
3097 "Dark Red Map Pin",
3098 "Yellow Map Pin",
3099 "Dark Yellow Map Pin",
3100 "Green Map Pin",
3101 "Dark Green Map Pin",
3102 "Turquoise Map Pin",
3103 "Dark Turquoise Map Pin",
3104 "Blue Map Pin",
3105 "Dark Blue Map Pin",
3106 // 10
3107 "Gray Map Pin",
3108 "Dark Gray Map Pin",
3109 "Red Flag",
3110 "Dark Red Flag",
3111 "Yellow Flag",
3112 "Dark Yellow Flag",
3113 "Green Flag",
3114 "Dark Green Flag",
3115 "Turquoise Flag",
3116 "Dark Turquoise Flag",
3117 // 20
3118 "Blue Flag",
3119 "Dark Blue Flag",
3120 "Gray Flag",
3121 "Dark Gray Flag",
3122 "Red Dot",
3123 "Dark Red Dot",
3124 "Yellow Dot",
3125 "Dark Yellow Dot",
3126 "Green Dot",
3127 "Dark Green Dot",
3128 // 30
3129 "Turquoise Dot",
3130 "Dark Turquoise Dot",
3131 "Blue Dot",
3132 "Dark Blue Dot",
3133 "Gray Dot",
3134 "Dark Gray Dot",
3135 "Small Red Dot",
3136 "Small Dark Red Dot",
3137 "Small Yellow Dot",
3138 "Small Dark Yellow Dot",
3139 // 40
3140 "Small Green Dot",
3141 "Small Dark Green Dot",
3142 "Small Turquoise Dot",
3143 "Small Dark Turquoise Dot",
3144 "Small Blue Dot",
3145 "Small Dark Blue Dot",
3146 "Small Gray Dot",
3147 "Small Dark Gray Dot",
3148 "Arrow Up",
3149 "Arrow Down",
3150 // 50
3151 "Arrow Left",
3152 "Arrow Right",
3153 "Arrow Up Left",
3154 "Arrow Up Right",
3155 "Arrow Down Left",
3156 "Arrow Down Right",
3157 "Green Star",
3158 "Yellow Square",
3159 "Red X",
3160 "Turquoise Circle",
3161 // 60
3162 "Purple Triangle",
3163 "American Flag",
3164 "Stop",
3165 "Parking",
3166 "First Aid",
3167 "Dining",
3168 "Railroad Crossing",
3169 "Heliport",
3170 "Restroom",
3171 "Information",
3172 // 70
3173 "Diver Down",
3174 "Exit",
3175 "Health Facility",
3176 "Police",
3177 "Post Office",
3178 "Mining",
3179 "Danger",
3180 "Money",
3181 "Exclamation",
3182 "Car",
3183 // 80
3184 "Jeep",
3185 "Truck",
3186 "Tow Truck",
3187 "Motor Home",
3188 "School Bus",
3189 "Four-wheeler",
3190 "Snowmobile",
3191 "Sailboat",
3192 "Powerboat",
3193 "Boat Launch",
3194 // 90
3195 "Anchor",
3196 "Buoy",
3197 "Shipwreck",
3198 "Glider Area",
3199 "Private Airport",
3200 "Public Airport",
3201 "Military Airport",
3202 "Military Base",
3203 "House",
3204 "Church",
3205 // 100
3206 "Building",
3207 "School",
3208 "Lighthouse",
3209 "Bridge",
3210 "Radio Tower",
3211 "Dam",
3212 "Tunnel",
3213 "Toll Booth",
3214 "Gas Station",
3215 "Lodging",
3216 // 110
3217 "Telephone",
3218 "Traffic Light",
3219 "Fire Hydrant",
3220 "Cemetery",
3221 "Picnic Table",
3222 "Tent",
3223 "Shelter",
3224 "Camper",
3225 "Fire",
3226 "Shower",
3227 // 120
3228 "Drinking Water",
3229 "Binoculars",
3230 "Camera",
3231 "Geocache",
3232 "Geocache Found",
3233 "Fishing Pole",
3234 "Ice Fishing Trap Set",
3235 "Ice Fishing Trap Up",
3236 "Moose",
3237 "Deer",
3238 // 130
3239 "Bear",
3240 "Bird",
3241 "Duck",
3242 "Fish",
3243 "Deer Tracks",
3244 "Animal Tracks",
3245 "Bird Tracks",
3246 "Birch Tree",
3247 "Evergreen Tree",
3248 "Deciduous Tree",
3249 // 140
3250 "Flower Garden",
3251 "Mountain",
3252 "Cave",
3253 "Beach",
3254 "Hiking",
3255 "Swimming",
3256 "Bicycling",
3257 "Kayaking",
3258 "Canoeing",
3259 "Water Skiing",
3260 // 150
3261 "Cross-country Skiing",
3262 "Downhill Skiing",
3263 "Ice Skating",
3264 "Dogsledding",
3265 "Shooting",
3266 "Golf Course",
3267 "Ballpark",
3268 // 157-182 added in PN-40 2.5 firmware
3269 "Cache Found",
3270 "Didn't Find It",
3271 "My Cache",
3272 // 160
3273 "Traditional Cache",
3274 "Multi-Cache",
3275 "Unknown Cache",
3276 "Letterbox Hybrid",
3277 "Whereigo Cache",
3278 "Event Cache",
3279 "Mega-Event Cache",
3280 "Cache In Trash Out Event",
3281 "EarthCache",
3282 "Virtual Cache",
3283 // 170
3284 "Webcam Cache",
3285 "Waymark",
3286 "NGS Benchmark",
3287 "Write Note",
3288 "Needs Maintenance",
3289 "Final Location",
3290 "Parking Area",
3291 "Question to Answer",
3292 "Reference Point",
3293 "Stages of a Multicache",
3294 // 180
3295 "Trailhead",
3296 "Temporarily Disable Listing",
3297 "Enable Listing",
3298 // 183-222 added in PN-40 2.7 firmware
3299 "Crane Truck",
3300 "Forest Fire",
3301 "Oil Derrick",
3302 "Wind Turbine",
3303 "Letter A",
3304 "Letter B",
3305 "Letter C",
3306 // 190
3307 "Letter D",
3308 "Letter E",
3309 "Letter F",
3310 "Letter G",
3311 "Letter H",
3312 "Letter I",
3313 "Letter J",
3314 "Letter K",
3315 "Letter L",
3316 "Letter M",
3317 // 200
3318 "Letter N",
3319 "Letter O",
3320 "Letter P",
3321 "Letter Q",
3322 "Letter R",
3323 "Letter S",
3324 "Letter T",
3325 "Letter U",
3326 "Letter V",
3327 "Letter W",
3328 // 210
3329 "Letter X",
3330 "Letter Y",
3331 "Letter Z",
3332 "Numeral 0",
3333 "Numeral 1",
3334 "Numeral 2",
3335 "Numeral 3",
3336 "Numeral 4",
3337 "Numeral 5",
3338 "Numeral 6",
3339 // 220
3340 "Numeral 7",
3341 "Numeral 8",
3342 "Numeral 9"
3343 };
3344
3345 static const char*
waypoint_symbol(unsigned i)3346 waypoint_symbol(unsigned i)
3347 {
3348 const char* p = NULL;
3349 if (i < sizeofarray(waypoint_symbol_name)) {
3350 p = waypoint_symbol_name[i];
3351 }
3352 return p;
3353 }
3354
3355 static unsigned
waypoint_symbol_index(const char * name)3356 waypoint_symbol_index(const char* name)
3357 {
3358 static unsigned last_result;
3359 static char last_name[32];
3360 unsigned i = last_result;
3361
3362 if (strncmp(name, last_name, sizeof(last_name)) != 0) {
3363 i = sizeofarray(waypoint_symbol_name);
3364 do {
3365 i--;
3366 } while (i != 0 && case_ignore_strcmp(name, waypoint_symbol_name[i]) != 0);
3367 strncpy(last_name, name, sizeof(last_name));
3368 last_result = i;
3369 }
3370 return i;
3371 }
3372
3373 // vi: ts=4 sw=4 noexpandtab
3374