/src/gpsd/gpsd-3.27.6~dev/drivers/driver_garmin.c
Line | Count | Source |
1 | | /* |
2 | | * This file contains two drivers for Garmin receivers and some code |
3 | | * shared by both drivers. |
4 | | * |
5 | | * One driver "garmin_usb_binary" handles the Garmin binary packet |
6 | | * format supported by the USB Garmins tested with the Garmin 18 and |
7 | | * other models. (There is also "garmin_usb_binary_old".) These are ONLY |
8 | | * for USB devices reporting as: 091e:0003. |
9 | | * |
10 | | * The other driver "garmin_ser_binary" is for Garmin receivers via a |
11 | | * serial port, whether or not one uses a USB/serial adaptor or a real |
12 | | * serial port. These receivers provide adequate NMEA support, so it |
13 | | * often makes sense to just put them into NMEA mode. |
14 | | * |
15 | | * On Linux, USB Garmins (091e:0003) need the Linux garmin_gps driver and |
16 | | * will not function without it. On other operating systems, it is clear |
17 | | * garmin_usb_binary_old does not work since it requires the Linux |
18 | | * garmin_gps module. |
19 | | * |
20 | | * This code has been tested and at least at one time is known to work on |
21 | | * big- and little-endian CPUs and 32 and 64 bit cpu modes. |
22 | | * |
23 | | * |
24 | | * Documentation for the Garmin protocols can be found via |
25 | | * http://www.garmin.com/support/commProtocol.html |
26 | | * The file IOSDK.zip contains IntfSpec.pdf, which describes the |
27 | | * protocol in terms of Application, Link, and Physical. This |
28 | | * identical file is also available at: |
29 | | * http://www.garmin.com/support/pdf/iop_spec.pdf |
30 | | * An older version of iop_spec.pdf that describes only Serial Binary |
31 | | * is available at: |
32 | | * http://vancouver-webpages.com/pub/peter/iop_spec.pdf |
33 | | * Information about the GPS 18 |
34 | | * http://www.garmin.com/manuals/425_TechnicalSpecification.pdf |
35 | | * |
36 | | * There is one physical link protocol for serial which uses DLE/ETX |
37 | | * framing. There is another physical protocol for USB which relies |
38 | | * on the packetization intrinsic to USB bulk pipes. |
39 | | * |
40 | | * There are several link protocols; all devices implement L000. |
41 | | * There are then product-specific protocols; most devices implement |
42 | | * L001. Link protocols are the same and carried over either Physical |
43 | | * protocol. |
44 | | * |
45 | | * Application protocols are named A000 and then with different |
46 | | * 3-digit numbers. They are carried over Link protocols. |
47 | | * |
48 | | * Thus, much of the higher-level code dealing the data formats is |
49 | | * shared between USB Binary and Serial Binary. |
50 | | * |
51 | | * This code is partly from the Garmin IOSDK and partly from the |
52 | | * sample code in the Linux garmin_gps driver. |
53 | | * |
54 | | * bad code by: Gary E. Miller <gem@rellim.com> |
55 | | * |
56 | | * -D 3 = packet trace |
57 | | * -D 4 = packet details |
58 | | * -D 5 = more packet details |
59 | | * -D 6 = very excessive details |
60 | | * |
61 | | * limitations: |
62 | | * |
63 | | * do not have from garmin: |
64 | | * pdop |
65 | | * hdop |
66 | | * vdop |
67 | | * magnetic variation |
68 | | * |
69 | | * known bugs: |
70 | | * hangs in the fread loop instead of keeping state and returning. |
71 | | * |
72 | | * This file is Copyright 2010 by the GPSD project |
73 | | * SPDX-License-Identifier: BSD-2-clause |
74 | | */ |
75 | | |
76 | | #include "../include/gpsd_config.h" // must be before all includes |
77 | | |
78 | | #ifdef GARMIN_ENABLE |
79 | | |
80 | | #include <errno.h> |
81 | | #include <math.h> |
82 | | #include <stdbool.h> |
83 | | #include <stdio.h> |
84 | | #include <string.h> |
85 | | #include <time.h> |
86 | | #include <unistd.h> |
87 | | |
88 | | #if defined(HAVE_LIBUSB) |
89 | | #include <libusb.h> |
90 | | #endif |
91 | | |
92 | | #include "../include/gpsd.h" |
93 | | #include "../include/bits.h" |
94 | | #include "../include/timespec.h" |
95 | | |
96 | 2.32k | #define GPSD_LE16TOH(x) getles16((char *)(&(x)), 0) |
97 | 122 | #define GPSD_LE32TOH(x) getles32((char *)(&(x)), 0) |
98 | 976 | #define GPSD_LEF32(x) getlef32((const char *)(&(x)), 0) |
99 | 366 | #define GPSD_LED64(x) getled64((const char *)(&(x)), 0) |
100 | | |
101 | | #define USE_RMD 0 |
102 | | |
103 | | // Used in Serial Physical Layer |
104 | 1.63k | #define ETX 0x03 |
105 | 2.10k | #define ACK 0x06 |
106 | 4.91k | #define DLE 0x10 |
107 | 1.09k | #define NAK 0x15 |
108 | | |
109 | | #define GARMIN_LAYERID_TRANSPORT (uint8_t) 0 |
110 | 38 | #define GARMIN_LAYERID_APPL (uint32_t) 20 |
111 | | // Linux Garmin USB driver layer-id to use for some control mechanisms |
112 | | #define GARMIN_LAYERID_PRIVATE 0x01106E4B |
113 | | |
114 | | // packet ids used in private layer |
115 | | #define PRIV_PKTID_SET_DEBUG 1 |
116 | | #define PRIV_PKTID_SET_MODE 2 |
117 | | #define PRIV_PKTID_INFO_REQ 3 |
118 | | #define PRIV_PKTID_INFO_RESP 4 |
119 | | #define PRIV_PKTID_RESET_REQ 5 |
120 | | #define PRIV_PKTID_SET_DEF_MODE 6 |
121 | | |
122 | | #define MODE_NATIVE 0 |
123 | | #define MODE_GARMIN_SERIAL 1 |
124 | | |
125 | | #define GARMIN_PKTID_TRANSPORT_START_SESSION_REQ 5 |
126 | | #define GARMIN_PKTID_TRANSPORT_START_SESSION_RESP 6 |
127 | | |
128 | 150 | #define GARMIN_PKTID_PROTOCOL_ARRAY 253 |
129 | 120 | #define GARMIN_PKTID_PRODUCT_RQST 254 |
130 | 131 | #define GARMIN_PKTID_PRODUCT_DATA 255 |
131 | | // 0x29 ')' |
132 | 303 | #define GARMIN_PKTID_RMD41_DATA 41 |
133 | | // 0x33 '3' |
134 | 188 | #define GARMIN_PKTID_PVT_DATA 51 |
135 | | // 0x33 '4' |
136 | 18 | #define GARMIN_PKTID_RMD_DATA 52 |
137 | | // 0x72 'r' |
138 | 94 | #define GARMIN_PKTID_SAT_DATA 114 |
139 | | |
140 | | #define GARMIN_PKTID_L001_XFER_CMPLT 12 |
141 | 38 | #define GARMIN_PKTID_L001_COMMAND_DATA 10 |
142 | | #define GARMIN_PKTID_L001_DATE_TIME_DATA 14 |
143 | | #define GARMIN_PKTID_L001_RECORDS 27 |
144 | | #define GARMIN_PKTID_L001_WPT_DATA 35 |
145 | | |
146 | 0 | #define CMND_ABORT 0 |
147 | 38 | #define CMND_START_PVT_DATA 49 |
148 | 0 | #define CMND_STOP_PVT_DATA 50 |
149 | 0 | #define CMND_START_RM_DATA 110 |
150 | | |
151 | | #define MAX_BUFFER_SIZE 4096 |
152 | | |
153 | 5.16k | #define GARMIN_CHANNELS 12 |
154 | | |
155 | | // something magic about 64, garmin driver will not return more than |
156 | | // 64 at a time. If you read less than 64 bytes the next read will |
157 | | // just get the last of the 64 byte buffer. |
158 | | #define ASYNC_DATA_SIZE 64 |
159 | | |
160 | | |
161 | | #pragma pack(1) |
162 | | // This is the data format of the satellite data from the garmin USB |
163 | | typedef struct __attribute__((__packed__)) |
164 | | { |
165 | | uint8_t svid; |
166 | | uint16_t snr; // 0 - 0xffff |
167 | | uint8_t elev; |
168 | | uint16_t azmth; |
169 | | uint8_t status; // bit 0, has ephemeris, 1, has diff correction |
170 | | // bit 2 used in solution |
171 | | // bit 3?? |
172 | | } cpo_sat_data; |
173 | | |
174 | | /* Garmin D800_Pvt_Datetype_Type |
175 | | * packet type: GARMIN_PKTID_PVT_DATA 51 |
176 | | * This is the data format of the position data from the garmin USB */ |
177 | | typedef struct __attribute__((__packed__)) |
178 | | { |
179 | | float alt; // altitude above WGS 84 (meters) |
180 | | float epe; // estimated position error, 2 sigma (meters) |
181 | | float eph; // epe, but horizontal only (meters) |
182 | | float epv; // epe but vertical only (meters ) |
183 | | int16_t fix; /* 0 - failed integrity check |
184 | | * 1 - invalid or unavailable fix |
185 | | * 2 - 2D |
186 | | * 3 - 3D |
187 | | * 4 - 2D Diff |
188 | | * 5 - 3D Diff |
189 | | */ |
190 | | double gps_tow; // gps time of week (seconds) |
191 | | double lat; // ->latitude (radians) |
192 | | double lon; // ->longitude (radians) |
193 | | float lon_vel; // velocity east (meters/second) |
194 | | float lat_vel; // velocity north (meters/second) |
195 | | float alt_vel; // velocity up (meters/sec) |
196 | | // Garmin GPS25 uses pkt_id 0x28 and does not output the |
197 | | // next 3 items |
198 | | float msl_hght; // height of WGS 84 above MSL (meters) |
199 | | int16_t leap_sec; // diff between GPS and UTC (seconds) |
200 | | int32_t grmn_days; /* days from UTC December 31st, 1989 to the |
201 | | * beginning of the current week */ |
202 | | } cpo_pvt_data; |
203 | | |
204 | | typedef struct __attribute__((__packed__)) |
205 | | { |
206 | | uint32_t cycles; |
207 | | double pr; // pseudorange in meters |
208 | | uint16_t phase; |
209 | | int8_t slp_dtct; |
210 | | uint8_t snr_dbhz; |
211 | | uint8_t svid; |
212 | | int8_t valid; |
213 | | } cpo_rcv_sv_data; |
214 | | |
215 | | /* packet type: GARMIN_PKTID_RMD_DATA 53 |
216 | | * seems identical to the packet id 0x29 from the Garmin GPS 25 */ |
217 | | typedef struct __attribute__((__packed__)) |
218 | | { |
219 | | double rcvr_tow; |
220 | | int16_t rcvr_wn; |
221 | | cpo_rcv_sv_data sv[GARMIN_CHANNELS]; |
222 | | } cpo_rcv_data; |
223 | | |
224 | | // This is the packet format to/from the Garmin USB |
225 | | typedef struct __attribute__((__packed__)) |
226 | | { |
227 | | uint8_t mPacketType; |
228 | | uint8_t mReserved1; |
229 | | uint16_t mReserved2; |
230 | | uint16_t mPacketId; |
231 | | uint16_t mReserved3; |
232 | | uint32_t mDataSize; |
233 | | union |
234 | | { |
235 | | //int8_t chars[MAX_BUFFER_SIZE]; |
236 | | // cppcheck-suppress unusedStructMember |
237 | | uint8_t uchars[MAX_BUFFER_SIZE]; |
238 | | cpo_pvt_data pvt; |
239 | | cpo_sat_data sats; |
240 | | } mData; |
241 | | } Packet_t; |
242 | | |
243 | | // useful funcs to read/write ints |
244 | | // floats and doubles are Intel (little-endian) order only... |
245 | | // FIXME: use include/bits.h instead |
246 | | static inline void set_int16(uint8_t * buf, uint32_t value) |
247 | 19 | { |
248 | 19 | buf[0] = (uint8_t) (0x0FF & value); |
249 | 19 | buf[1] = (uint8_t) (0x0FF & (value >> 8)); |
250 | 19 | } |
251 | | |
252 | | static inline void set_int32(uint8_t * buf, uint32_t value) |
253 | 0 | { |
254 | 0 | buf[0] = (uint8_t)(0x0FF & value); |
255 | 0 | buf[1] = (uint8_t)(0x0FF & (value >> 8)); |
256 | 0 | buf[2] = (uint8_t)(0x0FF & (value >> 16)); |
257 | 0 | buf[3] = (uint8_t)(0x0FF & (value >> 24)); |
258 | 0 | } |
259 | | |
260 | | static inline uint16_t get_uint16(const uint8_t * buf) |
261 | 87 | { |
262 | 87 | return (uint16_t)(0xFF & buf[0]) | |
263 | 87 | ((uint16_t)(0xFF & buf[1]) << 8); |
264 | 87 | } |
265 | | |
266 | | #if defined(HAVE_LIBUSB) && defined(__linux__) |
267 | | static inline uint32_t get_int32(const uint8_t * buf) |
268 | | { |
269 | | return (uint32_t)(0xFF & buf[0]) | |
270 | | ((uint32_t)(0xFF & buf[1]) << 8) | |
271 | | ((uint32_t)(0xFF & buf[2]) << 16) | |
272 | | ((uint32_t)(0xFF & buf[3]) << 24); |
273 | | } |
274 | | #endif // HAVE_LIBUSB |
275 | | |
276 | | // convert radians to degrees |
277 | | static inline double radtodeg(double rad) |
278 | 244 | { |
279 | 244 | return (double)(rad * RAD_2_DEG); |
280 | 244 | } |
281 | | |
282 | | static gps_mask_t PrintSERPacket(struct gps_device_t *session, |
283 | | unsigned char pkt_id, int pkt_len, |
284 | | unsigned char *buf); |
285 | | #if defined(HAVE_LIBUSB) && defined(__linux__) |
286 | | static gps_mask_t PrintUSBPacket(struct gps_device_t *session, |
287 | | Packet_t * pkt); |
288 | | #endif // HAVE_LIBUSB |
289 | | |
290 | | gps_mask_t PrintSERPacket(struct gps_device_t *session, unsigned char pkt_id, |
291 | | int pkt_len, unsigned char *buf) |
292 | 2.69k | { |
293 | | |
294 | 2.69k | gps_mask_t mask = ONLINE_SET; |
295 | 2.69k | int i = 0, j = 0; |
296 | 2.69k | uint16_t prod_id = 0; |
297 | 2.69k | uint16_t ver = 0; |
298 | 2.69k | int maj_ver; |
299 | 2.69k | int min_ver; |
300 | 2.69k | time_t time_l = 0; |
301 | 2.69k | char msg_buf[512] = ""; |
302 | 2.69k | char *msg = NULL; |
303 | 2.69k | cpo_sat_data *sats = NULL; |
304 | 2.69k | cpo_pvt_data *pvt = NULL; |
305 | 2.69k | cpo_rcv_data *rmd = NULL; |
306 | 2.69k | double gps_tow = 0; |
307 | | |
308 | 2.69k | GPSD_LOG(LOG_DATA, &session->context->errout, |
309 | 2.69k | "Garmin: PrintSERPacket(, %#02x, %#02x, )\n", pkt_id, pkt_len); |
310 | | |
311 | 2.69k | session->cycle_end_reliable = true; |
312 | | |
313 | 2.69k | switch (pkt_id) { |
314 | 1.05k | case ACK: |
315 | 1.05k | GPSD_LOG(LOG_PROG, &session->context->errout, "Garmin: ACK\n"); |
316 | 1.05k | break; |
317 | 548 | case NAK: |
318 | 548 | GPSD_LOG(LOG_PROG, &session->context->errout, "Garmin: NAK\n"); |
319 | 548 | break; |
320 | 19 | case GARMIN_PKTID_L001_COMMAND_DATA: |
321 | | // A010 |
322 | 19 | if (2 > pkt_len) { |
323 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
324 | 0 | "Garmin: %02x-: runt payload %d", |
325 | 0 | pkt_id, pkt_len); |
326 | 0 | return 0; |
327 | 0 | } |
328 | 19 | prod_id = get_uint16((uint8_t *) buf); |
329 | 19 | switch (prod_id) { |
330 | 0 | case CMND_ABORT: |
331 | 0 | msg = "Abort current xfer"; |
332 | 0 | break; |
333 | 19 | case CMND_START_PVT_DATA: |
334 | 19 | msg = "Start Xmit PVT data"; |
335 | 19 | break; |
336 | 0 | case CMND_STOP_PVT_DATA: |
337 | 0 | msg = "Stop Xmit PVT data"; |
338 | 0 | break; |
339 | 0 | case CMND_START_RM_DATA: |
340 | 0 | msg = "Start RMD data"; |
341 | 0 | break; |
342 | 0 | default: |
343 | 0 | (void)snprintf(msg_buf, sizeof(msg_buf), "Unknown: %u", |
344 | 0 | (unsigned int)prod_id); |
345 | 0 | msg = msg_buf; |
346 | 0 | break; |
347 | 19 | } |
348 | 19 | GPSD_LOG(LOG_PROG, &session->context->errout, |
349 | 19 | "Garmin: Appl, Command Data: %s\n", msg); |
350 | 19 | break; |
351 | 101 | case GARMIN_PKTID_PRODUCT_RQST: |
352 | 101 | GPSD_LOG(LOG_PROG, &session->context->errout, |
353 | 101 | "Garmin: Appl, Product Data req\n"); |
354 | 101 | break; |
355 | 131 | case GARMIN_PKTID_PRODUCT_DATA: |
356 | 131 | if (4 > pkt_len) { |
357 | 97 | GPSD_LOG(LOG_WARN, &session->context->errout, |
358 | 97 | "Garmin: %02x-: runt payload %d", |
359 | 97 | pkt_id, pkt_len); |
360 | 97 | return 0; |
361 | 97 | } |
362 | 34 | prod_id = get_uint16((uint8_t *) buf); |
363 | 34 | ver = get_uint16((uint8_t *) & buf[2]); |
364 | 34 | maj_ver = (int)(ver / 100); |
365 | 34 | min_ver = (int)(ver - (maj_ver * 100)); |
366 | 34 | GPSD_LOG(LOG_PROG, &session->context->errout, |
367 | 34 | "Garmin: Appl, Product Data, sz: %d\n", |
368 | 34 | pkt_len); |
369 | 34 | (void)snprintf(session->subtype, sizeof(session->subtype), |
370 | 34 | "%d: %d.%02d", (int)prod_id, maj_ver, min_ver); |
371 | 34 | GPSD_LOG(LOG_INF, &session->context->errout, |
372 | 34 | "Garmin: Product ID: %d, SoftVer: %d.%02d\n", |
373 | 34 | prod_id, maj_ver, min_ver); |
374 | 34 | GPSD_LOG(LOG_INF, &session->context->errout, |
375 | 34 | "Garmin: Product Desc: %s\n", &buf[4]); |
376 | 34 | mask |= DEVICEID_SET; |
377 | 34 | GPSD_LOG(LOG_DATA, &session->context->errout, |
378 | 34 | "Garmin: PRODUCT_DATA: subtype=%s\n", |
379 | 34 | session->subtype); |
380 | 34 | break; |
381 | 188 | case GARMIN_PKTID_PVT_DATA: |
382 | | // 0x33 (51) |
383 | 188 | if (64 > pkt_len) { |
384 | 66 | GPSD_LOG(LOG_WARN, &session->context->errout, |
385 | 66 | "Garmin: %02x-: runt payload %d", |
386 | 66 | pkt_id, pkt_len); |
387 | 66 | return 0; |
388 | 66 | } |
389 | 122 | GPSD_LOG(LOG_PROG, &session->context->errout, |
390 | 122 | "Garmin: PVT Data (51) Sz: %d\n", pkt_len); |
391 | | |
392 | 122 | pvt = (cpo_pvt_data *) buf; |
393 | | |
394 | 122 | session->context->leap_seconds = (int)GPSD_LE16TOH(pvt->leap_sec); |
395 | 122 | session->context->valid = LEAP_SECOND_VALID; |
396 | | |
397 | | // 631065600, unix seconds for 31 Dec 1989 Zulu |
398 | 122 | time_l = (time_t) (631065600 + (GPSD_LE32TOH(pvt->grmn_days) * 86400)); |
399 | | // TODO, convert grmn_days to context->gps_week |
400 | 122 | time_l -= session->context->leap_seconds; |
401 | | |
402 | | // gps_tow is always like x.999 or x.998 just round it to nearest sec |
403 | | // FIXME! this will break 5Hz garmins... |
404 | 122 | gps_tow = GPSD_LED64(pvt->gps_tow); |
405 | 122 | time_l += (time_t)round(gps_tow); |
406 | | |
407 | | /* sanity check unix time against leap second. |
408 | | * Leap second 18 at 1 Jan 2017: 1483228800 */ |
409 | 122 | if (17 < session->context->leap_seconds && |
410 | 55 | 1483228800L > time_l) { |
411 | 23 | time_l += 619315200; // fast forward 1024 weeks |
412 | 23 | } |
413 | | |
414 | 122 | DTOTS(&session->context->gps_tow, gps_tow); |
415 | 122 | session->newdata.time.tv_sec = time_l; |
416 | 122 | session->newdata.time.tv_nsec = 0; |
417 | | // (long long) for 32-bit systems |
418 | 122 | GPSD_LOG(LOG_PROG, &session->context->errout, |
419 | 122 | "Garmin: time_l: %lld\n", (long long)time_l); |
420 | | |
421 | 122 | session->newdata.latitude = radtodeg(GPSD_LED64(pvt->lat)); |
422 | 122 | session->newdata.longitude = radtodeg(GPSD_LED64(pvt->lon)); |
423 | | // altitude is WGS84 |
424 | 122 | session->newdata.altHAE = GPSD_LEF32(pvt->alt); |
425 | | |
426 | | // geoid separation from WGS 84 |
427 | | // gpsd sign is opposite of garmin sign |
428 | 122 | session->newdata.geoid_sep = -GPSD_LEF32(pvt->msl_hght); |
429 | | |
430 | | /* Estimated position error in meters. Confidence (sigma) not |
431 | | * specified by Garmin. |
432 | | * We follow the advice at <http://gpsinformation.net/main/errors.htm>. |
433 | | * Since GPS data is not gaussian, this is marginal advice... |
434 | | * If this assumption changes here, it should also change in |
435 | | * nmea_parse.c where we analyze PGRME. |
436 | | */ |
437 | 122 | session->newdata.sep = GPSD_LEF32(pvt->epe) * |
438 | 122 | (GPSD_CONFIDENCE / CEP50_SIGMA); |
439 | | // eph, horizaontal error, 2 sigma |
440 | 122 | session->newdata.eph = GPSD_LEF32(pvt->eph) * |
441 | 122 | (GPSD_CONFIDENCE / CEP50_SIGMA); |
442 | | // eph, horizaontal error, 2 sigma |
443 | 122 | session->newdata.epv = GPSD_LEF32(pvt->epv) * |
444 | 122 | (GPSD_CONFIDENCE / CEP50_SIGMA); |
445 | | |
446 | | // meters/sec |
447 | 122 | session->newdata.NED.velN = GPSD_LEF32(pvt->lat_vel); |
448 | 122 | session->newdata.NED.velE = GPSD_LEF32(pvt->lon_vel); |
449 | 122 | session->newdata.NED.velD = -GPSD_LEF32(pvt->alt_vel); |
450 | | |
451 | 122 | switch (GPSD_LE16TOH(pvt->fix)) { |
452 | 1 | case 0: |
453 | 1 | case 1: |
454 | 78 | default: |
455 | | // no fix |
456 | 78 | session->newdata.status = STATUS_UNK; |
457 | 78 | session->newdata.mode = MODE_NO_FIX; |
458 | 78 | break; |
459 | 0 | case 2: |
460 | | // 2D fix |
461 | 0 | session->newdata.status = STATUS_GPS; |
462 | 0 | session->newdata.mode = MODE_2D; |
463 | 0 | break; |
464 | 44 | case 3: |
465 | | // 3D fix |
466 | 44 | session->newdata.status = STATUS_GPS; |
467 | 44 | session->newdata.mode = MODE_3D; |
468 | 44 | break; |
469 | 0 | case 4: |
470 | | // 2D Differential fix |
471 | 0 | session->newdata.status = STATUS_DGPS; |
472 | 0 | session->newdata.mode = MODE_2D; |
473 | 0 | break; |
474 | 0 | case 5: |
475 | | // 3D differential fix |
476 | 0 | session->newdata.status = STATUS_DGPS; |
477 | 0 | session->newdata.mode = MODE_3D; |
478 | 0 | break; |
479 | 122 | } |
480 | | |
481 | 122 | GPSD_LOG(LOG_PROG, &session->context->errout, |
482 | 122 | "Garmin: Appl, mode %d, status %d\n", |
483 | 122 | session->newdata.mode, session->newdata.status); |
484 | | |
485 | | // save some expensive calculations if not needed |
486 | 122 | if (session->context->errout.debug >= LOG_INF) { |
487 | |
|
488 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
489 | 0 | "Garmin: UTC Time: %lld\n", |
490 | 0 | (long long)session->newdata.time.tv_sec); |
491 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
492 | 0 | "Garmin: Geoid Separation (MSL-WGS84): from garmin %lf, " |
493 | 0 | "calculated %lf\n", |
494 | 0 | session->newdata.geoid_sep, |
495 | 0 | wgs84_separation(session->newdata.latitude, |
496 | 0 | session->newdata.longitude)); |
497 | |
|
498 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
499 | 0 | "Garmin: Alt: %.3f, sep: %.3f, eph: %.3f, Epv: %.3f, " |
500 | 0 | "Fix: %d, Gps_tow: %f, Lat: %.3f, Lon: %.3f, " |
501 | 0 | "velN: %.3f, velE: %.3f, velD: %.3f, geoidsep: %.3f, " |
502 | 0 | "Leap: %d, GarminDays: %ld\n", |
503 | 0 | session->newdata.altHAE, |
504 | 0 | session->newdata.sep, |
505 | 0 | session->newdata.eph, |
506 | 0 | session->newdata.epv, |
507 | 0 | GPSD_LE16TOH(pvt->fix), |
508 | 0 | gps_tow, |
509 | 0 | session->newdata.latitude, |
510 | 0 | session->newdata.longitude, |
511 | 0 | session->newdata.NED.velN, |
512 | 0 | session->newdata.NED.velE, |
513 | 0 | session->newdata.NED.velD, |
514 | 0 | session->newdata.geoid_sep, |
515 | 0 | session->context->leap_seconds, |
516 | 0 | GPSD_LE32TOH(pvt->grmn_days)); |
517 | 0 | } |
518 | | |
519 | 122 | if (session->newdata.mode > MODE_NO_FIX) { |
520 | | // data only valid with a fix |
521 | 44 | mask |= |
522 | 44 | TIME_SET | LATLON_SET | ALTITUDE_SET | STATUS_SET | MODE_SET | |
523 | 44 | HERR_SET | PERR_IS | CLEAR_IS | REPORT_IS | VNED_SET; |
524 | | /* |
525 | | * Garmin documentation says we should wait until four good fixes |
526 | | * have been seen before trying to use the device for precision |
527 | | * time service. |
528 | | */ |
529 | 44 | if (session->fixcnt > 3) |
530 | 22 | mask |= NTPTIME_IS; |
531 | 44 | } |
532 | 122 | GPSD_LOG(LOG_DATA, &session->context->errout, |
533 | 122 | "Garmin: PVT_DATA: time=%lld, lat=%.2f lon=%.2f " |
534 | 122 | "eph=%.2f sep=%.2f epv=%.2f mode=%d status=%d\n", |
535 | 122 | (long long)session->newdata.time.tv_sec, |
536 | 122 | session->newdata.latitude, |
537 | 122 | session->newdata.longitude, |
538 | 122 | session->newdata.eph, |
539 | 122 | session->newdata.sep, |
540 | 122 | session->newdata.epv, |
541 | 122 | session->newdata.mode, |
542 | 122 | session->newdata.status); |
543 | 122 | break; |
544 | 18 | case GARMIN_PKTID_RMD_DATA: |
545 | 18 | FALLTHROUGH |
546 | 303 | case GARMIN_PKTID_RMD41_DATA: |
547 | 303 | rmd = (cpo_rcv_data *) buf; |
548 | 303 | GPSD_LOG(LOG_DATA, &session->context->errout, |
549 | 303 | "Garmin: PVT RMD Data Sz: %d\n", pkt_len); |
550 | 303 | GPSD_LOG(LOG_PROG, &session->context->errout, |
551 | 303 | "Garmin: PVT RMD rcvr_tow: %f, rcvr_wn: %d\n", |
552 | 303 | GPSD_LED64(rmd->rcvr_tow), GPSD_LE16TOH(rmd->rcvr_wn)); |
553 | 3.93k | for (i = 0; i < GARMIN_CHANNELS; i++) { |
554 | 3.63k | GPSD_LOG(LOG_INF, &session->context->errout, |
555 | 3.63k | "Garmin: PVT RMD Sat: %3u, cycles: %9lu, pr: %16.6f, " |
556 | 3.63k | "phase: %7.3f, slp_dtct: %3s, snr: %3u, Valid: %3s\n", |
557 | 3.63k | (int)rmd->sv[i].svid + 1, |
558 | 3.63k | GPSD_LE32TOH(rmd->sv[i].cycles), |
559 | 3.63k | GPSD_LED64(rmd->sv[i].pr), |
560 | 3.63k | (GPSD_LE16TOH(rmd->sv[i].phase) * 360.0) / 2048.0, |
561 | 3.63k | rmd->sv[i].slp_dtct != 0 ? "Yes" : "No", |
562 | 3.63k | rmd->sv[i].snr_dbhz, |
563 | 3.63k | rmd->sv[i].valid != 0 ? "Yes" : "No"); |
564 | 3.63k | } |
565 | 303 | break; |
566 | | |
567 | 94 | case GARMIN_PKTID_SAT_DATA: |
568 | | // record ID 0x72 (114) |
569 | 94 | GPSD_LOG(LOG_PROG, &session->context->errout, |
570 | 94 | "Garmin: SAT Data Sz: %d\n", pkt_len); |
571 | 94 | sats = (cpo_sat_data *) buf; |
572 | | |
573 | 94 | session->gpsdata.satellites_used = 0; |
574 | 94 | gpsd_zero_satellites(&session->gpsdata); |
575 | 1.22k | for (i = 0, j = 0; i < GARMIN_CHANNELS; i++, sats++) { |
576 | 1.12k | GPSD_LOG(LOG_INF, &session->context->errout, |
577 | 1.12k | "Garmin: Sat %2d, snr: %5u, elev: %2d, Azmth: %3d, " |
578 | 1.12k | "Stat: x%x\n", |
579 | 1.12k | sats->svid, GPSD_LE16TOH(sats->snr), sats->elev, |
580 | 1.12k | GPSD_LE16TOH(sats->azmth), |
581 | 1.12k | sats->status); |
582 | | |
583 | 1.12k | if (255 == (int)sats->svid) { |
584 | | // Garmin uses 255 for empty |
585 | | // gpsd uses 0 for empty |
586 | 43 | continue; |
587 | 43 | } |
588 | | |
589 | 1.08k | if ((int)sats->svid <= 32) { |
590 | | // GPS 1-32 |
591 | 839 | session->gpsdata.skyview[j].PRN = (short)sats->svid; |
592 | 839 | session->gpsdata.skyview[j].svid = (short)sats->svid; |
593 | 839 | session->gpsdata.skyview[j].gnssid = GNSSID_GPS; |
594 | 839 | } else { |
595 | | // SBAS 33-64 |
596 | 246 | session->gpsdata.skyview[j].PRN = (short)sats->svid; |
597 | 246 | session->gpsdata.skyview[j].svid = (short)sats->svid + 87; |
598 | 246 | session->gpsdata.skyview[j].gnssid = GNSSID_SBAS; |
599 | 246 | } |
600 | 1.08k | session->gpsdata.skyview[j].azimuth = |
601 | 1.08k | (short)GPSD_LE16TOH(sats->azmth); |
602 | 1.08k | session->gpsdata.skyview[j].elevation = (short)sats->elev; |
603 | 1.08k | if (0xffff == sats->snr) { |
604 | 89 | session->gpsdata.skyview[j].ss = NAN; |
605 | 996 | } else { |
606 | | // Garmin does not document this. snr is in dB*100 |
607 | | // Known, but not seen satellites have a dB value of -1*100 |
608 | 996 | session->gpsdata.skyview[j].ss = |
609 | 996 | (float)(GPSD_LE16TOH(sats->snr) / 100.0); |
610 | 996 | } |
611 | | // FIX-ME: Garmin documents this, but Daniel Dorau |
612 | | // <daniel.dorau@gmx.de> says the behavior on his GPSMap60CSX |
613 | | // doesn't match it. |
614 | 1.08k | if ((uint8_t) 0 != (sats->status & 4)) { |
615 | | // used in solution? |
616 | 163 | session->gpsdata.skyview[j].used = true; |
617 | 163 | session->gpsdata.satellites_used++; |
618 | 163 | } |
619 | 1.08k | session->gpsdata.satellites_visible++; |
620 | 1.08k | j++; |
621 | | |
622 | 1.08k | } |
623 | 94 | session->gpsdata.skyview_time.tv_sec = 0; |
624 | 94 | session->gpsdata.skyview_time.tv_nsec = 0; |
625 | 94 | mask |= USED_IS | SATELLITE_SET; |
626 | 94 | GPSD_LOG(LOG_DATA, &session->context->errout, |
627 | 94 | "Garmin: SAT_DATA: visible=%d used=%d\n", |
628 | 94 | session->gpsdata.satellites_visible, |
629 | 94 | session->gpsdata.satellites_used); |
630 | 94 | break; |
631 | 150 | case GARMIN_PKTID_PROTOCOL_ARRAY: |
632 | | // Pid_Protocol_Array, ID 253 |
633 | | // this packet is never requested, it just comes, in some case |
634 | | // after a GARMIN_PKTID_PRODUCT_RQST |
635 | 150 | GPSD_LOG(LOG_INF, &session->context->errout, |
636 | 150 | "Garmin: Appl, Product Capability, sz: %d\n", |
637 | 150 | pkt_len); |
638 | 334 | for (i = 0; i < pkt_len; i += 3) { |
639 | 184 | GPSD_LOG(LOG_INF, &session->context->errout, |
640 | 184 | "Garmin: %c%03d\n", |
641 | 184 | buf[i], get_uint16((uint8_t *) & buf[i + 1])); |
642 | 184 | } |
643 | 150 | break; |
644 | 104 | default: |
645 | 104 | GPSD_LOG(LOG_WARN, &session->context->errout, |
646 | 104 | "Garmin: Unknown packet id: %#02x, Sz: %#02x\n", |
647 | 104 | pkt_id, pkt_len); |
648 | 104 | break; |
649 | 2.69k | } |
650 | 2.52k | GPSD_LOG(LOG_DATA, &session->context->errout, |
651 | 2.52k | "Garmin: PrintSERPacket(, %#02x, %#02x, ) mask=(%s)\n", |
652 | 2.52k | pkt_id, pkt_len, gps_maskdump(mask)); |
653 | 2.52k | return mask; |
654 | 2.69k | } |
655 | | |
656 | | |
657 | | #if defined(HAVE_LIBUSB) && defined(__linux__) |
658 | | // For debugging, decodes and prints some known packets |
659 | | static gps_mask_t PrintUSBPacket(struct gps_device_t *session, Packet_t * pkt) |
660 | | { |
661 | | gps_mask_t mask = 0; |
662 | | int maj_ver; |
663 | | int min_ver; |
664 | | uint32_t mode = 0; |
665 | | uint16_t prod_id = 0; |
666 | | uint32_t veri = 0; |
667 | | uint32_t serial; |
668 | | uint32_t mDataSize = get_int32((uint8_t *) & pkt->mDataSize); |
669 | | uint8_t *buffer = (uint8_t *) pkt; |
670 | | |
671 | | GPSD_LOG(LOG_PROG, &session->context->errout, "Garmin: PrintUSBPacket()\n"); |
672 | | if (DLE == pkt->mPacketType) { |
673 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
674 | | "Garmin: really a SER packet!\n"); |
675 | | return PrintSERPacket(session, |
676 | | (unsigned char)buffer[1], |
677 | | (int)buffer[2], (unsigned char *)(buffer + 3)); |
678 | | } |
679 | | |
680 | | if (4096 < mDataSize) { |
681 | | GPSD_LOG(LOG_WARN, &session->context->errout, |
682 | | "Garmin: bogus packet, size too large=%d\n", |
683 | | mDataSize); |
684 | | return 0; |
685 | | } |
686 | | |
687 | | switch (pkt->mPacketType) { |
688 | | case GARMIN_LAYERID_TRANSPORT: |
689 | | // Garmin USB layer specific |
690 | | switch (pkt->mPacketId) { |
691 | | case GARMIN_PKTID_TRANSPORT_START_SESSION_REQ: |
692 | | // Pid_Start_Session, ID 5 |
693 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
694 | | "Garmin: Transport, Start Session req\n"); |
695 | | break; |
696 | | case GARMIN_PKTID_TRANSPORT_START_SESSION_RESP: |
697 | | // Pid_Session_Started, ID 6 |
698 | | mode = get_int32(&pkt->mData.uchars[0]); |
699 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
700 | | "Garmin: Transport, Start Session resp, unit: 0x%x\n", |
701 | | mode); |
702 | | break; |
703 | | default: |
704 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
705 | | "Garmin: Transport, Packet: Type %d %d %d, ID: %d," |
706 | | "Sz: %d\n", |
707 | | pkt->mPacketType, pkt->mReserved1, pkt->mReserved2, |
708 | | pkt->mPacketId, mDataSize); |
709 | | break; |
710 | | } |
711 | | break; |
712 | | case GARMIN_LAYERID_APPL: |
713 | | // raw data transport, shared with Garmin Serial Driver |
714 | | |
715 | | mask = PrintSERPacket(session, |
716 | | (unsigned char)pkt->mPacketId, |
717 | | (int)mDataSize, |
718 | | (unsigned char *)pkt->mData.uchars); |
719 | | break; |
720 | | case 75: |
721 | | // private, garmin USB kernel driver specific |
722 | | switch (pkt->mPacketId) { |
723 | | case PRIV_PKTID_SET_MODE: |
724 | | prod_id = get_uint16(&pkt->mData.uchars[0]); |
725 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
726 | | "Garmin: Private, Set Mode: %d\n", prod_id); |
727 | | break; |
728 | | case PRIV_PKTID_INFO_REQ: |
729 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
730 | | "Garmin: Private, ID: Info Req\n"); |
731 | | break; |
732 | | case PRIV_PKTID_INFO_RESP: |
733 | | veri = get_int32(pkt->mData.uchars); |
734 | | maj_ver = (int)(veri >> 16); |
735 | | min_ver = (int)(veri & 0xffff); |
736 | | mode = get_int32(&pkt->mData.uchars[4]); |
737 | | serial = get_int32(&pkt->mData.uchars[8]); |
738 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
739 | | "Garmin: Private, ID: Info Resp\n"); |
740 | | GPSD_LOG(LOG_INF, &session->context->errout, |
741 | | "Garmin: USB Driver Version %d.%d Mode %d, Serial# %u\n", |
742 | | maj_ver, min_ver, mode, serial); |
743 | | break; |
744 | | default: |
745 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
746 | | "Garmin: Private, Packet: ID: %d, Sz: %d\n", |
747 | | pkt->mPacketId, mDataSize); |
748 | | break; |
749 | | } |
750 | | break; |
751 | | default: |
752 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
753 | | "Garmin: Packet: Type %d %d %d, ID: %d, Sz: %d\n", |
754 | | pkt->mPacketType, pkt->mReserved1, pkt->mReserved2, |
755 | | pkt->mPacketId, mDataSize); |
756 | | break; |
757 | | } |
758 | | |
759 | | return mask; |
760 | | } |
761 | | |
762 | | #endif // HAVE_LIBUSB |
763 | | |
764 | | |
765 | | #if defined(HAVE_LIBUSB) && defined(__linux__) |
766 | | // build and send a packet w/ USB protocol |
767 | | static void Build_Send_USB_Packet(struct gps_device_t *session, |
768 | | uint32_t layer_id, uint32_t pkt_id, |
769 | | uint32_t length, uint32_t data) |
770 | | { |
771 | | uint8_t *buffer = (uint8_t *) session->driver.garmin.Buffer; |
772 | | Packet_t *thePacket = (Packet_t *) buffer; |
773 | | ssize_t theBytesReturned = 0; |
774 | | ssize_t theBytesToWrite = 12 + (ssize_t) length; |
775 | | |
776 | | set_int32(buffer, layer_id); |
777 | | set_int32(buffer + 4, pkt_id); |
778 | | set_int32(buffer + 8, length); |
779 | | if (2 == length) { |
780 | | set_int16(buffer + 12, data); |
781 | | } else if (4 == length) { |
782 | | set_int32(buffer + 12, data); |
783 | | } |
784 | | (void)PrintUSBPacket(session, thePacket); |
785 | | |
786 | | theBytesReturned = gpsd_write(session, (const char *)thePacket, |
787 | | (size_t) theBytesToWrite); |
788 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
789 | | "Garmin: SendPacket(), wrote %zd bytes\n", |
790 | | theBytesReturned); |
791 | | |
792 | | // Garmin says: |
793 | | // If the packet size was an exact multiple of the USB packet |
794 | | // size, we must make a final write call with no data |
795 | | |
796 | | // as a practical matter no known packets are 64 bytes long so |
797 | | // this is untested |
798 | | |
799 | | // So here goes just in case |
800 | | if (0 == (theBytesToWrite % ASYNC_DATA_SIZE)) { |
801 | | char *n = ""; |
802 | | (void)gpsd_write(session, n, 0); |
803 | | } |
804 | | } |
805 | | #endif // HAVE_LIBUSB && __linux__ |
806 | | |
807 | | /* build and send a packet in serial protocol |
808 | | * layer_id unused */ |
809 | | // FIX-ME: This should go through the common message buffer someday |
810 | | static void Build_Send_SER_Packet(struct gps_device_t *session, |
811 | | uint32_t layer_id UNUSED, uint32_t pkt_id, |
812 | | uint32_t length, uint32_t data) |
813 | 1.63k | { |
814 | 1.63k | uint8_t *buffer = (uint8_t *) session->driver.garmin.Buffer; |
815 | 1.63k | uint8_t *buffer0 = buffer; |
816 | 1.63k | Packet_t *thePacket = (Packet_t *) buffer; |
817 | 1.63k | ssize_t theBytesReturned = 0; |
818 | 1.63k | ssize_t theBytesToWrite = 6 + (ssize_t) length; |
819 | 1.63k | uint8_t chksum = 0; |
820 | | |
821 | 1.63k | *buffer++ = (uint8_t) DLE; |
822 | 1.63k | *buffer++ = (uint8_t) pkt_id; |
823 | 1.63k | chksum = pkt_id; |
824 | 1.63k | *buffer++ = (uint8_t) length; |
825 | 1.63k | chksum += length; |
826 | | // ??? What is this doing? |
827 | 1.63k | if (2 == length) { |
828 | | // careful! no DLE stuffing here! |
829 | 19 | set_int16(buffer, data); |
830 | 19 | chksum += buffer[0]; |
831 | 19 | chksum += buffer[1]; |
832 | 1.61k | } else if (4 == length) { |
833 | | // careful! no DLE stuffing here! |
834 | 0 | set_int32(buffer, data); |
835 | 0 | chksum += buffer[0]; |
836 | 0 | chksum += buffer[1]; |
837 | 0 | chksum += buffer[2]; |
838 | 0 | chksum += buffer[3]; |
839 | 0 | } |
840 | | // ??? How is data copied to the buffer? |
841 | 1.63k | buffer += length; |
842 | | |
843 | | // Add checksum |
844 | 1.63k | *buffer++ = -chksum; |
845 | 1.63k | if (DLE == -chksum) { |
846 | | // stuff another DLE |
847 | 0 | *buffer++ = (uint8_t) DLE; |
848 | 0 | theBytesToWrite++; |
849 | 0 | } |
850 | | // Add DLE, ETX |
851 | 1.63k | *buffer++ = (uint8_t) DLE; |
852 | | // we used to say n++ here, but scan-build complains |
853 | 1.63k | *buffer = (uint8_t) ETX; |
854 | | |
855 | 1.63k | (void)PrintSERPacket(session, |
856 | 1.63k | (unsigned char)buffer0[1], |
857 | 1.63k | (int)buffer0[2], (unsigned char *)(buffer0 + 3)); |
858 | | |
859 | 1.63k | theBytesReturned = gpsd_write(session, (const char *)thePacket, |
860 | 1.63k | (size_t) theBytesToWrite); |
861 | 1.63k | GPSD_LOG(LOG_PROG, &session->context->errout, |
862 | 1.63k | "Garmin: SendPacket(), wrote %zd bytes\n", |
863 | 1.63k | theBytesReturned); |
864 | | |
865 | 1.63k | } |
866 | | |
867 | | #if defined(HAVE_LIBUSB) && defined(__linux__) |
868 | | /* |
869 | | * is_usb_device() - is a specified device USB matching given vendor/product? |
870 | | * |
871 | | * BUG: Doesn't actually match against path yet. Must finish this function |
872 | | * by querying /sys/dev/char, either directly or using libudev. Greg KH |
873 | | * assures this is possible, though he is vague about how. |
874 | | * |
875 | | * libudev: http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/ |
876 | | */ |
877 | | static bool is_usb_device(const char *path UNUSED, int vendor, int product, |
878 | | struct gpsd_errout_t *errout) |
879 | | { |
880 | | // discover devices |
881 | | libusb_device **list; |
882 | | ssize_t cnt; |
883 | | ssize_t i = 0; |
884 | | bool found = false; |
885 | | |
886 | | GPSD_LOG(LOG_INF, errout, "attempting USB device enumeration.\n"); |
887 | | (void)libusb_init(NULL); |
888 | | |
889 | | if (0 > (cnt = libusb_get_device_list(NULL, &list))) { |
890 | | GPSD_LOG(LOG_ERROR, errout, "USB device list call failed.\n"); |
891 | | libusb_exit(NULL); |
892 | | return false; |
893 | | } |
894 | | |
895 | | for (i = 0; i < cnt; i++) { |
896 | | struct libusb_device_descriptor desc; |
897 | | libusb_device *dev = list[i]; |
898 | | |
899 | | int r = libusb_get_device_descriptor(dev, &desc); |
900 | | if (0 > r) { |
901 | | GPSD_LOG(LOG_ERROR, errout, |
902 | | "USB descriptor fetch failed on device %zd.\n", i); |
903 | | continue; |
904 | | } |
905 | | |
906 | | // we can extract device descriptor data |
907 | | GPSD_LOG(LOG_INF, errout, |
908 | | "%04x:%04x (bus %d, device %d)\n", |
909 | | desc.idVendor, desc.idProduct, |
910 | | libusb_get_bus_number(dev), |
911 | | libusb_get_device_address(dev)); |
912 | | |
913 | | // we match if vendor and product ID are right |
914 | | if (desc.idVendor == (uint16_t)vendor && |
915 | | desc.idProduct == (uint16_t)product) { |
916 | | found = true; |
917 | | break; |
918 | | } |
919 | | } |
920 | | |
921 | | GPSD_LOG(LOG_INF, errout, |
922 | | "vendor/product match with %04x:%04x %sfound\n", |
923 | | vendor, product, found ? "" : "not "); |
924 | | libusb_free_device_list(list, 1); |
925 | | libusb_exit(NULL); |
926 | | return found; |
927 | | } |
928 | | |
929 | | #endif // HAVE_LIBUSB |
930 | | |
931 | | /* |
932 | | * garmin_usb_detect() - detect a Garmin USB device connected to session fd. |
933 | | * |
934 | | * This is ONLY for USB devices reporting as: 091e:0003. |
935 | | * |
936 | | * This driver ONLY works in Linux and ONLY when the garmin_gps kernel |
937 | | * module is installed. |
938 | | * |
939 | | * This is only necessary because under Linux Garmin USB devices need a |
940 | | * kernel module rather than being normal USB-serial devices. |
941 | | * |
942 | | * The actual wire protocol from the Garmin device is very strange. There |
943 | | * are no delimiters. End of packet is signaled by a zero-length read |
944 | | * on the USB device, and start of packet is the next read. You can't just |
945 | | * ignore the zero reads and pass the data through - you'd never be able |
946 | | * to tell where the packet boundaries are. |
947 | | * |
948 | | * The garmin_usb module's job is to grab the packet and frame it in |
949 | | * DLEs (with DLE stuffing). This makes the USB packets look as |
950 | | * though they came from a regular Garmin *serial* device, which is how |
951 | | * most of the processing for both types can be unified here. |
952 | | * |
953 | | * return 1 is device found |
954 | | * return 0 if not |
955 | | */ |
956 | | static bool garmin_usb_detect(struct gps_device_t *session UNUSED) |
957 | 0 | { |
958 | 0 | #if defined(__linux__) |
959 | | /* |
960 | | * Only perform this check if we're looking at a USB-serial |
961 | | * device. This prevents drivers for attached serial GPSes |
962 | | * fronm being rudely elbowed aside by this one if they happen |
963 | | * to be trying to coexist with the Garmin. |
964 | | */ |
965 | 0 | if (SOURCE_USB != session->sourcetype) { |
966 | 0 | return false; |
967 | 0 | } |
968 | | |
969 | | #ifdef HAVE_LIBUSB |
970 | | if (!is_usb_device(session->gpsdata.dev.path, 0x091e, 0x0003, |
971 | | &session->context->errout)) { |
972 | | return false; |
973 | | } |
974 | | |
975 | | if (!gpsd_set_raw(session)) { |
976 | | GPSD_LOG(LOG_ERROR, &session->context->errout, |
977 | | "Garmin: garmin_usb_detect: error changing port " |
978 | | "attributes: %s\n", |
979 | | strerror(errno)); |
980 | | return false; |
981 | | } |
982 | | |
983 | | if (sizeof(session->driver.garmin.Buffer) < sizeof(Packet_t)) { |
984 | | // dunno how this happens, but it does on some compilers |
985 | | GPSD_LOG(LOG_ERROR, &session->context->errout, |
986 | | "Garmin: garmin_usb_detect: Compile error, " |
987 | | "garmin.Buffer too small.\n"); |
988 | | return false; |
989 | | } |
990 | | |
991 | | // FIXME!!! needs to use libusb totally and move garmin_gps aside */ |
992 | | // set Mode 1, mode 0 is broken somewhere past 2.6.14 |
993 | | // but how? |
994 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
995 | | "Garmin: Set garmin_gps driver mode = 0\n"); |
996 | | Build_Send_USB_Packet(session, GARMIN_LAYERID_PRIVATE, |
997 | | PRIV_PKTID_SET_MODE, 4, MODE_GARMIN_SERIAL); |
998 | | // expect no return packet !? |
999 | | |
1000 | | return true; |
1001 | | #endif // HAVE_LIBUSB |
1002 | 0 | #endif // __linux__ |
1003 | 0 | return false; |
1004 | 0 | } |
1005 | | |
1006 | | static void garmin_event_hook(struct gps_device_t *session, event_t event) |
1007 | 2.03k | { |
1008 | 2.03k | if (session->context->readonly || |
1009 | 1.40k | session->context->passive) { |
1010 | 1.40k | return; |
1011 | 1.40k | } |
1012 | | /* |
1013 | | * FIX-ME: It might not be necessary to call this on reactivate. |
1014 | | * Experiment to see if the holds its settings through a close. |
1015 | | */ |
1016 | 629 | if (event == EVENT_IDENTIFIED || |
1017 | 610 | event == EVENT_REACTIVATE) { |
1018 | | // Tell the device to send product data |
1019 | 19 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1020 | 19 | "Garmin: Get Product Data\n"); |
1021 | 19 | Build_Send_SER_Packet(session, GARMIN_LAYERID_APPL, |
1022 | 19 | GARMIN_PKTID_PRODUCT_RQST, 0, 0); |
1023 | | |
1024 | | // turn on PVT data 49 |
1025 | 19 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1026 | 19 | "Garmin: Set to send reports every 1 second\n"); |
1027 | | |
1028 | 19 | Build_Send_SER_Packet(session, GARMIN_LAYERID_APPL, |
1029 | 19 | GARMIN_PKTID_L001_COMMAND_DATA, 2, |
1030 | 19 | CMND_START_PVT_DATA); |
1031 | | |
1032 | | #if USE_RMD |
1033 | | // turn on RMD data 110 |
1034 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
1035 | | "Garmin: Set to send Raw sat data\n"); |
1036 | | Build_Send_SER_Packet(session, GARMIN_LAYERID_APPL, |
1037 | | GARMIN_PKTID_L001_COMMAND_DATA, 2, |
1038 | | CMND_START_RM_DATA); |
1039 | | #endif |
1040 | 19 | } |
1041 | 629 | if (event == EVENT_DEACTIVATE) { |
1042 | | // FIX-ME: is any action needed, or is closing the port sufficient? |
1043 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1044 | 0 | "Garmin: garmin_close()\n"); |
1045 | 0 | } |
1046 | 629 | } |
1047 | | |
1048 | 1.05k | #define Send_ACK() Build_Send_SER_Packet(session, 0, ACK, 0, 0) |
1049 | 548 | #define Send_NAK() Build_Send_SER_Packet(session, 0, NAK, 0, 0) |
1050 | | |
1051 | | gps_mask_t garmin_ser_parse(struct gps_device_t *session) |
1052 | 1.60k | { |
1053 | 1.60k | unsigned char *buf = session->lexer.outbuffer; |
1054 | 1.60k | size_t len = session->lexer.outbuflen; |
1055 | 1.60k | unsigned char data_buf[300]; // Max Garmin payload is 255 |
1056 | 1.60k | unsigned char c; |
1057 | 1.60k | int i = 0; |
1058 | 1.60k | size_t n = 0; |
1059 | 1.60k | int data_index = 0; |
1060 | 1.60k | int got_dle = 0; |
1061 | 1.60k | unsigned char pkt_id = 0; |
1062 | 1.60k | unsigned char pkt_len = 0; |
1063 | 1.60k | unsigned char chksum = 0; |
1064 | 1.60k | gps_mask_t mask = 0; |
1065 | 1.60k | struct timespec delay; |
1066 | | |
1067 | 1.60k | GPSD_LOG(LOG_RAW, &session->context->errout, |
1068 | 1.60k | "Garmin: garmin_ser_parse()\n"); |
1069 | 1.60k | if (6 > len) { |
1070 | | // WTF? |
1071 | | // minimum packet; <DLE> [pkt id] [length=0] [chksum] <DLE> <STX> |
1072 | 73 | Send_NAK(); |
1073 | 73 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1074 | 73 | "Garmin: serial too short: %zd\n", len); |
1075 | 73 | return 0; |
1076 | 73 | } |
1077 | | // debug |
1078 | 34.0k | for (i = 0; i < (int)len; i++) { |
1079 | 32.4k | GPSD_LOG(LOG_RAW, &session->context->errout, |
1080 | 32.4k | "Garmin: Char: %#02x\n", buf[i]); |
1081 | 32.4k | } |
1082 | | |
1083 | 1.52k | if ('\x10' != buf[0]) { |
1084 | 91 | Send_NAK(); |
1085 | 91 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1086 | 91 | "Garmin: buf[0] not DLE\n"); |
1087 | 91 | return 0; |
1088 | 91 | } |
1089 | 1.43k | n = 1; |
1090 | 1.43k | pkt_id = buf[n++]; |
1091 | 1.43k | chksum = pkt_id; |
1092 | 1.43k | if ('\x10' == pkt_id) { |
1093 | 0 | if ('\x10' != buf[n++]) { |
1094 | 0 | Send_NAK(); |
1095 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1096 | 0 | "Garmin: Bad pkt_id %#02x\n", pkt_id); |
1097 | 0 | return 0; |
1098 | 0 | } |
1099 | 0 | } |
1100 | | |
1101 | 1.43k | pkt_len = buf[n++]; |
1102 | 1.43k | chksum += pkt_len; |
1103 | 1.43k | if ('\x10' == pkt_len) { |
1104 | 91 | if ('\x10' != buf[n++]) { |
1105 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1106 | 0 | "Garmin: Bad pkt_len %#02x\n", pkt_len); |
1107 | 0 | Send_NAK(); |
1108 | 0 | return 0; |
1109 | 0 | } |
1110 | 91 | } |
1111 | 1.43k | data_index = 0; |
1112 | | // we are never really sure of garmin packet sizes, prevent another CVE |
1113 | 1.43k | memset(data_buf, 0, sizeof(data_buf)); |
1114 | 23.7k | for (i = 0; i < 256; i++) { |
1115 | | |
1116 | 23.7k | if ((int)pkt_len == data_index) { |
1117 | | // got it all |
1118 | 1.21k | break; |
1119 | 1.21k | } |
1120 | 22.4k | if (len < n + i) { |
1121 | 20 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1122 | 20 | "Garmin: Packet too short %zd < %zd\n", |
1123 | 20 | len, n + i); |
1124 | 20 | Send_NAK(); |
1125 | 20 | return 0; |
1126 | 20 | } |
1127 | 22.4k | c = buf[n + i]; |
1128 | 22.4k | if (got_dle) { |
1129 | | // handle DLE stuffing |
1130 | 1.02k | got_dle = 0; |
1131 | 1.02k | if ('\x10' != c) { |
1132 | 205 | Send_NAK(); |
1133 | 205 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1134 | 205 | "Garmin: Bad DLE %#02x\n", c); |
1135 | 205 | return 0; |
1136 | 205 | } |
1137 | 21.4k | } else { |
1138 | 21.4k | chksum += c; |
1139 | 21.4k | data_buf[data_index++] = c; |
1140 | 21.4k | if ('\x10' == c) { |
1141 | 1.17k | got_dle = 1; |
1142 | 1.17k | } |
1143 | 21.4k | } |
1144 | 22.4k | } |
1145 | | // get checksum |
1146 | 1.21k | if (len < n + i) { |
1147 | 10 | Send_NAK(); |
1148 | 10 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1149 | 10 | "Garmin: No checksum, Packet too short %zd < %zd\n", len, |
1150 | 10 | n + i); |
1151 | 10 | return 0; |
1152 | 10 | } |
1153 | 1.20k | c = buf[n + i++]; |
1154 | 1.20k | chksum += c; |
1155 | | // get final DLE |
1156 | 1.20k | if (len < n + i) { |
1157 | 11 | Send_NAK(); |
1158 | 11 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1159 | 11 | "Garmin: No final DLE, Packet too short %zd < %zd\n", len, |
1160 | 11 | n + i); |
1161 | 11 | return 0; |
1162 | 11 | } |
1163 | 1.19k | c = buf[n + i++]; |
1164 | 1.19k | if ('\x10' != c) { |
1165 | 72 | Send_NAK(); |
1166 | 72 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1167 | 72 | "Garmin: Final DLE not DLE\n"); |
1168 | 72 | return 0; |
1169 | 72 | } |
1170 | | // get final ETX |
1171 | 1.11k | if (len < n + i) { |
1172 | 0 | Send_NAK(); |
1173 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1174 | 0 | "Garmin: No final ETX, Packet too short %zd < %zd\n", len, |
1175 | 0 | n + i); |
1176 | 0 | return 0; |
1177 | 0 | } |
1178 | | // we used to say n++ here, but scan-build complains |
1179 | 1.11k | c = buf[n + i]; |
1180 | 1.11k | if ('\x03' != c) { |
1181 | 66 | Send_NAK(); |
1182 | 66 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1183 | 66 | "Garmin: Final ETX not ETX\n"); |
1184 | 66 | return 0; |
1185 | 66 | } |
1186 | | |
1187 | | // debug |
1188 | 19.2k | for (i = 0; i < data_index; i++) { |
1189 | 18.2k | GPSD_LOG(LOG_RAW, &session->context->errout, |
1190 | 18.2k | "Garmin: Char %#02x\n", data_buf[i]); |
1191 | 18.2k | } |
1192 | | |
1193 | | |
1194 | 1.05k | GPSD_LOG(LOG_DATA, &session->context->errout, |
1195 | 1.05k | "Garmin: garmin_ser_parse() Type %#02x Len %#02x chksum %#02x\n", |
1196 | 1.05k | pkt_id, pkt_len, chksum); |
1197 | 1.05k | mask = PrintSERPacket(session, pkt_id, pkt_len, data_buf); |
1198 | | |
1199 | | /* sending ACK too soon might hang the session |
1200 | | * so send ACK last, after a pause, then wait 300 uSec */ |
1201 | 1.05k | delay.tv_sec = 0; |
1202 | 1.05k | delay.tv_nsec = 300000L; |
1203 | 1.05k | nanosleep(&delay, NULL); |
1204 | | |
1205 | 1.05k | Send_ACK(); |
1206 | 1.05k | GPSD_LOG(LOG_DATA, &session->context->errout, |
1207 | 1.05k | "Garmin: garmin_ser_parse( )\n"); |
1208 | 1.05k | return mask; |
1209 | 1.11k | } |
1210 | | |
1211 | | |
1212 | | static void settle(void) |
1213 | 0 | { |
1214 | 0 | struct timespec delay, rem; |
1215 | 0 | memset(&delay, 0, sizeof(delay)); |
1216 | 0 | delay.tv_sec = 0; |
1217 | 0 | delay.tv_nsec = 333000000L; |
1218 | 0 | nanosleep(&delay, &rem); |
1219 | 0 | } |
1220 | | |
1221 | | static void garmin_switcher(struct gps_device_t *session, int mode) |
1222 | 0 | { |
1223 | 0 | if (mode == MODE_NMEA) { |
1224 | 0 | const char switcher[] = |
1225 | 0 | { 0x10, 0x0A, 0x02, 0x26, 0x00, 0xCE, 0x10, 0x03 }; |
1226 | | // Note hard-coded string length in the next line... |
1227 | 0 | ssize_t status = gpsd_write(session, switcher, sizeof(switcher)); |
1228 | 0 | if ((ssize_t)sizeof(switcher) == status) { |
1229 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1230 | 0 | "Garmin: => GPS: turn off binary %02x %02x %02x... \n", |
1231 | 0 | switcher[0], switcher[1], switcher[2]); |
1232 | 0 | } else { |
1233 | 0 | GPSD_LOG(LOG_ERROR, &session->context->errout, |
1234 | 0 | "Garmin: => GPS: FAILED\n"); |
1235 | 0 | } |
1236 | 0 | settle(); // wait 333mS, essential! |
1237 | | |
1238 | | // once a sec, no binary, no averaging, NMEA 2.3, WAAS |
1239 | 0 | (void)nmea_send(session, "$PGRMC1,1,1"); |
1240 | | //(void)nmea_send(fd, "$PGRMC1,1,1,1,,,,2,W,N"); |
1241 | 0 | (void)nmea_send(session, "$PGRMI,,,,,,,R"); |
1242 | 0 | settle(); // wait 333mS, essential! |
1243 | 0 | } else { |
1244 | 0 | (void)nmea_send(session, "$PGRMC1,1,2,1,,,,2,W,N"); |
1245 | 0 | (void)nmea_send(session, "$PGRMI,,,,,,,R"); |
1246 | 0 | settle(); // wait 333mS, essential! |
1247 | 0 | } |
1248 | 0 | } |
1249 | | |
1250 | | // not used by the daemon, it's for gpsctl and friends |
1251 | | static ssize_t garmin_control_send(struct gps_device_t *session, |
1252 | | char *buf, size_t buflen) |
1253 | 0 | { |
1254 | 0 | session->msgbuflen = buflen; |
1255 | 0 | (void)memcpy(session->msgbuf, buf, buflen); |
1256 | 0 | return gpsd_write(session, session->msgbuf, session->msgbuflen); |
1257 | 0 | } |
1258 | | |
1259 | | static double garmin_time_offset(struct gps_device_t *session) |
1260 | 0 | { |
1261 | 0 | if (SOURCE_USB == session->sourcetype) { |
1262 | 0 | return 0.035; // Garmin USB, expect +/- 40mS jitter |
1263 | 0 | } |
1264 | | /* only two sentences ships time |
1265 | | * but the PVT data is always first */ |
1266 | 0 | switch (session->gpsdata.dev.baudrate) { |
1267 | 0 | case 4800: |
1268 | 0 | return 0.430; // TBD |
1269 | 0 | case 9600: |
1270 | 0 | return 0.430; // tested 12Arp10 |
1271 | 0 | case 19200: |
1272 | 0 | return 0.430; // TBD |
1273 | 0 | case 38400: |
1274 | 0 | return 0.430; // TBD |
1275 | 0 | } |
1276 | 0 | return 0.430; // WTF? WAG |
1277 | 0 | } |
1278 | | |
1279 | | // this is everything we export |
1280 | | |
1281 | | // *INDENT-OFF* |
1282 | | const struct gps_type_t driver_garmin_usb_binary = |
1283 | | { |
1284 | | .type_name = "Garmin USB binary", // full name of type |
1285 | | .packet_type = GARMIN_PACKET, // associated lexer packet type |
1286 | | .flags = DRIVER_STICKY, // remember this |
1287 | | .trigger = NULL, // no trigger, it has a probe |
1288 | | .channels = GARMIN_CHANNELS, // consumer-grade GPS |
1289 | | .probe_detect = garmin_usb_detect, // how to detect at startup time |
1290 | | .get_packet = packet_get1, // how to grab a packet |
1291 | | .parse_packet = garmin_ser_parse, // parse message packets |
1292 | | .rtcm_writer = NULL, // don't send DGPS corrections |
1293 | | .init_query = NULL, // non-perturbing initial query |
1294 | | .event_hook = garmin_event_hook, // lifetime ebent handler |
1295 | | .speed_switcher = NULL, // no speed switcher |
1296 | | .mode_switcher = NULL, // Garmin USB Binary has no NMEA |
1297 | | .rate_switcher = NULL, // no sample-rate switcher |
1298 | | .min_cycle.tv_sec = 0, |
1299 | | .min_cycle.tv_nsec = 10000000, // 10Hz |
1300 | | .control_send = garmin_control_send, // send raw bytes |
1301 | | .time_offset = garmin_time_offset, |
1302 | | }; |
1303 | | |
1304 | | const struct gps_type_t driver_garmin_ser_binary = |
1305 | | { |
1306 | | .type_name = "Garmin Serial binary", // full name of type |
1307 | | .packet_type = GARMIN_PACKET, // associated lexer packet type |
1308 | | .flags = DRIVER_STICKY, // remember this |
1309 | | .trigger = NULL, // no trigger, it has a probe |
1310 | | .channels = GARMIN_CHANNELS, // consumer-grade GPS |
1311 | | .probe_detect = NULL, // how to detect at startup time |
1312 | | .get_packet = packet_get1, // how to grab a packet |
1313 | | .parse_packet = garmin_ser_parse, // parse message packets |
1314 | | .rtcm_writer = NULL, // don't send DGPS corrections |
1315 | | .init_query = NULL, // non-perturbing initial query |
1316 | | /* The Garmin Gek0 301 needs to be kicked to start sending binary. |
1317 | | * like the Garmin USB. */ |
1318 | | .event_hook = garmin_event_hook, // lifetime event handler |
1319 | | .speed_switcher = NULL, // no speed switcher |
1320 | | .mode_switcher = garmin_switcher, // how to change modes |
1321 | | .rate_switcher = NULL, // no sample-rate switcher |
1322 | | .min_cycle.tv_sec = 0, |
1323 | | .min_cycle.tv_nsec = 10000000, // 10Hz |
1324 | | .control_send = garmin_control_send, // send raw bytes |
1325 | | .time_offset = garmin_time_offset, |
1326 | | }; |
1327 | | // *INDENT-ON* |
1328 | | |
1329 | | #endif // GARMIN_ENABLE |
1330 | | |
1331 | | // vim: set expandtab shiftwidth=4 |