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