/src/gpsd/gpsd-3.25.1~dev/drivers/driver_garmin.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #define GPSD_LE16TOH(x) getles16((char *)(&(x)), 0) |
97 | 0 | #define GPSD_LE32TOH(x) getles32((char *)(&(x)), 0) |
98 | 0 | #define GPSD_LEF32(x) getlef32((const char *)(&(x)), 0) |
99 | 0 | #define GPSD_LED64(x) getled64((const char *)(&(x)), 0) |
100 | | |
101 | | #define USE_RMD 0 |
102 | | |
103 | | // Used in Serial Physical Layer |
104 | 0 | #define ETX 0x03 |
105 | 0 | #define ACK 0x06 |
106 | 0 | #define DLE 0x10 |
107 | 0 | #define NAK 0x15 |
108 | | |
109 | | #define GARMIN_LAYERID_TRANSPORT (uint8_t) 0 |
110 | 0 | #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 | 0 | #define GARMIN_PKTID_PROTOCOL_ARRAY 253 |
129 | 0 | #define GARMIN_PKTID_PRODUCT_RQST 254 |
130 | 0 | #define GARMIN_PKTID_PRODUCT_DATA 255 |
131 | | // 0x29 ')' |
132 | 0 | #define GARMIN_PKTID_RMD41_DATA 41 |
133 | | // 0x33 '3' |
134 | 0 | #define GARMIN_PKTID_PVT_DATA 51 |
135 | | // 0x33 '4' |
136 | 0 | #define GARMIN_PKTID_RMD_DATA 52 |
137 | | // 0x72 'r' |
138 | 0 | #define GARMIN_PKTID_SAT_DATA 114 |
139 | | |
140 | | #define GARMIN_PKTID_L001_XFER_CMPLT 12 |
141 | 0 | #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 | 0 | #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 | 0 | #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 52 |
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 | 0 | { |
248 | 0 | buf[0] = (uint8_t) (0x0FF & value); |
249 | 0 | buf[1] = (uint8_t) (0x0FF & (value >> 8)); |
250 | 0 | } |
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 | 0 | { |
262 | 0 | return (uint16_t)(0xFF & buf[0]) | |
263 | 0 | ((uint16_t)(0xFF & buf[1]) << 8); |
264 | 0 | } |
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 | 0 | { |
279 | 0 | return (double)(rad * RAD_2_DEG); |
280 | 0 | } |
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 | 0 | { |
293 | |
|
294 | 0 | gps_mask_t mask = ONLINE_SET; |
295 | 0 | int i = 0, j = 0; |
296 | 0 | uint16_t prod_id = 0; |
297 | 0 | uint16_t ver = 0; |
298 | 0 | int maj_ver; |
299 | 0 | int min_ver; |
300 | 0 | time_t time_l = 0; |
301 | 0 | char msg_buf[512] = ""; |
302 | 0 | char *msg = NULL; |
303 | 0 | cpo_sat_data *sats = NULL; |
304 | 0 | cpo_pvt_data *pvt = NULL; |
305 | 0 | cpo_rcv_data *rmd = NULL; |
306 | 0 | double gps_tow = 0; |
307 | |
|
308 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
309 | 0 | "Garmin: PrintSERPacket(, %#02x, %#02x, )\n", pkt_id, pkt_len); |
310 | |
|
311 | 0 | session->cycle_end_reliable = true; |
312 | |
|
313 | 0 | switch (pkt_id) { |
314 | 0 | case ACK: |
315 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, "Garmin: ACK\n"); |
316 | 0 | break; |
317 | 0 | case NAK: |
318 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, "Garmin: NAK\n"); |
319 | 0 | break; |
320 | 0 | case GARMIN_PKTID_L001_COMMAND_DATA: |
321 | 0 | prod_id = get_uint16((uint8_t *) buf); |
322 | 0 | switch (prod_id) { |
323 | 0 | case CMND_ABORT: |
324 | 0 | msg = "Abort current xfer"; |
325 | 0 | break; |
326 | 0 | case CMND_START_PVT_DATA: |
327 | 0 | msg = "Start Xmit PVT data"; |
328 | 0 | break; |
329 | 0 | case CMND_STOP_PVT_DATA: |
330 | 0 | msg = "Stop Xmit PVT data"; |
331 | 0 | break; |
332 | 0 | case CMND_START_RM_DATA: |
333 | 0 | msg = "Start RMD data"; |
334 | 0 | break; |
335 | 0 | default: |
336 | 0 | (void)snprintf(msg_buf, sizeof(msg_buf), "Unknown: %u", |
337 | 0 | (unsigned int)prod_id); |
338 | 0 | msg = msg_buf; |
339 | 0 | break; |
340 | 0 | } |
341 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
342 | 0 | "Garmin: Appl, Command Data: %s\n", msg); |
343 | 0 | break; |
344 | 0 | case GARMIN_PKTID_PRODUCT_RQST: |
345 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
346 | 0 | "Garmin: Appl, Product Data req\n"); |
347 | 0 | break; |
348 | 0 | case GARMIN_PKTID_PRODUCT_DATA: |
349 | 0 | prod_id = get_uint16((uint8_t *) buf); |
350 | 0 | ver = get_uint16((uint8_t *) & buf[2]); |
351 | 0 | maj_ver = (int)(ver / 100); |
352 | 0 | min_ver = (int)(ver - (maj_ver * 100)); |
353 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
354 | 0 | "Garmin: Appl, Product Data, sz: %d\n", |
355 | 0 | pkt_len); |
356 | 0 | (void)snprintf(session->subtype, sizeof(session->subtype), |
357 | 0 | "%d: %d.%02d", (int)prod_id, maj_ver, min_ver); |
358 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
359 | 0 | "Garmin: Product ID: %d, SoftVer: %d.%02d\n", |
360 | 0 | prod_id, maj_ver, min_ver); |
361 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
362 | 0 | "Garmin: Product Desc: %s\n", &buf[4]); |
363 | 0 | mask |= DEVICEID_SET; |
364 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
365 | 0 | "Garmin: PRODUCT_DATA: subtype=%s\n", |
366 | 0 | session->subtype); |
367 | 0 | break; |
368 | 0 | case GARMIN_PKTID_PVT_DATA: |
369 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
370 | 0 | "Garmin: PVT Data (51) Sz: %d\n", pkt_len); |
371 | |
|
372 | 0 | pvt = (cpo_pvt_data *) buf; |
373 | |
|
374 | 0 | session->context->leap_seconds = (int)GPSD_LE16TOH(pvt->leap_sec); |
375 | 0 | session->context->valid = LEAP_SECOND_VALID; |
376 | | |
377 | | // 631065600, unix seconds for 31 Dec 1989 Zulu |
378 | 0 | time_l = (time_t) (631065600 + (GPSD_LE32TOH(pvt->grmn_days) * 86400)); |
379 | | // TODO, convert grmn_days to context->gps_week |
380 | 0 | time_l -= session->context->leap_seconds; |
381 | | |
382 | | // gps_tow is always like x.999 or x.998 just round it to nearest sec |
383 | | // FIXME! this will break 5Hz garmins... |
384 | 0 | gps_tow = GPSD_LED64(pvt->gps_tow); |
385 | 0 | time_l += (time_t)round(gps_tow); |
386 | | |
387 | | /* sanity check unix time against leap second. |
388 | | * Leap second 18 at 1 Jan 2017: 1483228800 */ |
389 | 0 | if (17 < session->context->leap_seconds && |
390 | 0 | 1483228800L > time_l) { |
391 | 0 | time_l += 619315200; // fast forward 1024 weeks |
392 | 0 | } |
393 | |
|
394 | 0 | DTOTS(&session->context->gps_tow, gps_tow); |
395 | 0 | session->newdata.time.tv_sec = time_l; |
396 | 0 | session->newdata.time.tv_nsec = 0; |
397 | | // (long long) for 32-bit systems |
398 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
399 | 0 | "Garmin: time_l: %lld\n", (long long)time_l); |
400 | |
|
401 | 0 | session->newdata.latitude = radtodeg(GPSD_LED64(pvt->lat)); |
402 | 0 | session->newdata.longitude = radtodeg(GPSD_LED64(pvt->lon)); |
403 | | // altitude is WGS84 |
404 | 0 | session->newdata.altHAE = GPSD_LEF32(pvt->alt); |
405 | | |
406 | | // geoid separation from WGS 84 |
407 | | // gpsd sign is opposite of garmin sign |
408 | 0 | session->newdata.geoid_sep = -GPSD_LEF32(pvt->msl_hght); |
409 | | |
410 | | /* Estimated position error in meters. Confidence (sigma) not |
411 | | * specified by Garmin. |
412 | | * We follow the advice at <http://gpsinformation.net/main/errors.htm>. |
413 | | * Since GPS data is not gaussian, this is marginal advice... |
414 | | * If this assumption changes here, it should also change in |
415 | | * nmea_parse.c where we analyze PGRME. |
416 | | */ |
417 | 0 | session->newdata.sep = GPSD_LEF32(pvt->epe) * |
418 | 0 | (GPSD_CONFIDENCE / CEP50_SIGMA); |
419 | | // eph, horizaontal error, 2 sigma |
420 | 0 | session->newdata.eph = GPSD_LEF32(pvt->eph) * |
421 | 0 | (GPSD_CONFIDENCE / CEP50_SIGMA); |
422 | | // eph, horizaontal error, 2 sigma |
423 | 0 | session->newdata.epv = GPSD_LEF32(pvt->epv) * |
424 | 0 | (GPSD_CONFIDENCE / CEP50_SIGMA); |
425 | | |
426 | | /* meters/sec */ |
427 | 0 | session->newdata.NED.velN = GPSD_LEF32(pvt->lat_vel); |
428 | 0 | session->newdata.NED.velE = GPSD_LEF32(pvt->lon_vel); |
429 | 0 | session->newdata.NED.velD = -GPSD_LEF32(pvt->alt_vel); |
430 | |
|
431 | 0 | switch (GPSD_LE16TOH(pvt->fix)) { |
432 | 0 | case 0: |
433 | 0 | case 1: |
434 | 0 | default: |
435 | | // no fix |
436 | 0 | session->newdata.status = STATUS_UNK; |
437 | 0 | session->newdata.mode = MODE_NO_FIX; |
438 | 0 | break; |
439 | 0 | case 2: |
440 | | // 2D fix |
441 | 0 | session->newdata.status = STATUS_GPS; |
442 | 0 | session->newdata.mode = MODE_2D; |
443 | 0 | break; |
444 | 0 | case 3: |
445 | | // 3D fix |
446 | 0 | session->newdata.status = STATUS_GPS; |
447 | 0 | session->newdata.mode = MODE_3D; |
448 | 0 | break; |
449 | 0 | case 4: |
450 | | // 2D Differential fix |
451 | 0 | session->newdata.status = STATUS_DGPS; |
452 | 0 | session->newdata.mode = MODE_2D; |
453 | 0 | break; |
454 | 0 | case 5: |
455 | | // 3D differential fix |
456 | 0 | session->newdata.status = STATUS_DGPS; |
457 | 0 | session->newdata.mode = MODE_3D; |
458 | 0 | break; |
459 | 0 | } |
460 | | |
461 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
462 | 0 | "Garmin: Appl, mode %d, status %d\n", |
463 | 0 | session->newdata.mode, session->newdata.status); |
464 | | |
465 | | // save some expensive calculations if not needed |
466 | 0 | if (session->context->errout.debug >= LOG_INF) { |
467 | |
|
468 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
469 | 0 | "Garmin: UTC Time: %lld\n", |
470 | 0 | (long long)session->newdata.time.tv_sec); |
471 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
472 | 0 | "Garmin: Geoid Separation (MSL-WGS84): from garmin %lf, " |
473 | 0 | "calculated %lf\n", |
474 | 0 | session->newdata.geoid_sep, |
475 | 0 | wgs84_separation(session->newdata.latitude, |
476 | 0 | session->newdata.longitude)); |
477 | |
|
478 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
479 | 0 | "Garmin: Alt: %.3f, sep: %.3f, eph: %.3f, Epv: %.3f, " |
480 | 0 | "Fix: %d, Gps_tow: %f, Lat: %.3f, Lon: %.3f, " |
481 | 0 | "velN: %.3f, velE: %.3f, velD: %.3f, geoidsep: %.3f, " |
482 | 0 | "Leap: %d, GarminDays: %d\n", |
483 | 0 | session->newdata.altHAE, |
484 | 0 | session->newdata.sep, |
485 | 0 | session->newdata.eph, |
486 | 0 | session->newdata.epv, |
487 | 0 | GPSD_LE16TOH(pvt->fix), |
488 | 0 | gps_tow, |
489 | 0 | session->newdata.latitude, |
490 | 0 | session->newdata.longitude, |
491 | 0 | session->newdata.NED.velN, |
492 | 0 | session->newdata.NED.velE, |
493 | 0 | session->newdata.NED.velD, |
494 | 0 | session->newdata.geoid_sep, |
495 | 0 | session->context->leap_seconds, |
496 | 0 | GPSD_LE32TOH(pvt->grmn_days)); |
497 | 0 | } |
498 | |
|
499 | 0 | if (session->newdata.mode > MODE_NO_FIX) { |
500 | | // data only valid with a fix |
501 | 0 | mask |= |
502 | 0 | TIME_SET | LATLON_SET | ALTITUDE_SET | STATUS_SET | MODE_SET | |
503 | 0 | HERR_SET | PERR_IS | CLEAR_IS | REPORT_IS | VNED_SET; |
504 | | /* |
505 | | * Garmin documentation says we should wait until four good fixes |
506 | | * have been seen before trying to use the device for precision |
507 | | * time service. |
508 | | */ |
509 | 0 | if (session->fixcnt > 3) |
510 | 0 | mask |= NTPTIME_IS; |
511 | 0 | } |
512 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
513 | 0 | "Garmin: PVT_DATA: time=%lld, lat=%.2f lon=%.2f " |
514 | 0 | "eph=%.2f sep=%.2f epv=%.2f mode=%d status=%d\n", |
515 | 0 | (long long)session->newdata.time.tv_sec, |
516 | 0 | session->newdata.latitude, |
517 | 0 | session->newdata.longitude, |
518 | 0 | session->newdata.eph, |
519 | 0 | session->newdata.sep, |
520 | 0 | session->newdata.epv, |
521 | 0 | session->newdata.mode, |
522 | 0 | session->newdata.status); |
523 | 0 | break; |
524 | 0 | case GARMIN_PKTID_RMD_DATA: |
525 | 0 | case GARMIN_PKTID_RMD41_DATA: |
526 | 0 | rmd = (cpo_rcv_data *) buf; |
527 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
528 | 0 | "Garmin: PVT RMD Data Sz: %d\n", pkt_len); |
529 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
530 | 0 | "Garmin: PVT RMD rcvr_tow: %f, rcvr_wn: %d\n", |
531 | 0 | GPSD_LED64(rmd->rcvr_tow), GPSD_LE16TOH(rmd->rcvr_wn)); |
532 | 0 | for (i = 0; i < GARMIN_CHANNELS; i++) { |
533 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
534 | 0 | "Garmin: PVT RMD Sat: %3u, cycles: %9u, pr: %16.6f, " |
535 | 0 | "phase: %7.3f, slp_dtct: %3s, snr: %3u, Valid: %3s\n", |
536 | 0 | (int)rmd->sv[i].svid + 1, |
537 | 0 | GPSD_LE32TOH(rmd->sv[i].cycles), |
538 | 0 | GPSD_LED64(rmd->sv[i].pr), |
539 | 0 | (GPSD_LE16TOH(rmd->sv[i].phase) * 360.0) / 2048.0, |
540 | 0 | rmd->sv[i].slp_dtct != 0 ? "Yes" : "No", |
541 | 0 | rmd->sv[i].snr_dbhz, |
542 | 0 | rmd->sv[i].valid != 0 ? "Yes" : "No"); |
543 | 0 | } |
544 | 0 | break; |
545 | | |
546 | 0 | case GARMIN_PKTID_SAT_DATA: |
547 | | // record ID 0x72 (114) |
548 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
549 | 0 | "Garmin: SAT Data Sz: %d\n", pkt_len); |
550 | 0 | sats = (cpo_sat_data *) buf; |
551 | |
|
552 | 0 | session->gpsdata.satellites_used = 0; |
553 | 0 | gpsd_zero_satellites(&session->gpsdata); |
554 | 0 | for (i = 0, j = 0; i < GARMIN_CHANNELS; i++, sats++) { |
555 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
556 | 0 | "Garmin: Sat %2d, snr: %5u, elev: %2d, Azmth: %3d, " |
557 | 0 | "Stat: x%x\n", |
558 | 0 | sats->svid, GPSD_LE16TOH(sats->snr), sats->elev, |
559 | 0 | GPSD_LE16TOH(sats->azmth), |
560 | 0 | sats->status); |
561 | |
|
562 | 0 | if (255 == (int)sats->svid) { |
563 | | // Garmin uses 255 for empty |
564 | | // gpsd uses 0 for empty |
565 | 0 | continue; |
566 | 0 | } |
567 | | |
568 | 0 | if ((int)sats->svid <= 32) { |
569 | | /* GPS 1-32 */ |
570 | 0 | session->gpsdata.skyview[j].PRN = (short)sats->svid; |
571 | 0 | session->gpsdata.skyview[j].svid = (short)sats->svid; |
572 | 0 | session->gpsdata.skyview[j].gnssid = GNSSID_GPS; |
573 | 0 | } else { |
574 | | /* SBAS 33-64 */ |
575 | 0 | session->gpsdata.skyview[j].PRN = (short)sats->svid; |
576 | 0 | session->gpsdata.skyview[j].svid = (short)sats->svid + 87; |
577 | 0 | session->gpsdata.skyview[j].gnssid = GNSSID_SBAS; |
578 | 0 | } |
579 | 0 | session->gpsdata.skyview[j].azimuth = |
580 | 0 | (short)GPSD_LE16TOH(sats->azmth); |
581 | 0 | session->gpsdata.skyview[j].elevation = (short)sats->elev; |
582 | 0 | if (0xffff == sats->snr) { |
583 | 0 | session->gpsdata.skyview[j].ss = NAN; |
584 | 0 | } else { |
585 | | // Garmin does not document this. snr is in dB*100 |
586 | | // Known, but not seen satellites have a dB value of -1*100 |
587 | 0 | session->gpsdata.skyview[j].ss = |
588 | 0 | (float)(GPSD_LE16TOH(sats->snr) / 100.0); |
589 | 0 | } |
590 | | // FIX-ME: Garmin documents this, but Daniel Dorau |
591 | | // <daniel.dorau@gmx.de> says the behavior on his GPSMap60CSX |
592 | | // doesn't match it. |
593 | 0 | if ((uint8_t) 0 != (sats->status & 4)) { |
594 | | // used in solution? |
595 | 0 | session->gpsdata.skyview[j].used = true; |
596 | 0 | session->gpsdata.satellites_used++; |
597 | 0 | } |
598 | 0 | session->gpsdata.satellites_visible++; |
599 | 0 | j++; |
600 | |
|
601 | 0 | } |
602 | 0 | session->gpsdata.skyview_time.tv_sec = 0; |
603 | 0 | session->gpsdata.skyview_time.tv_nsec = 0; |
604 | 0 | mask |= USED_IS | SATELLITE_SET; |
605 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
606 | 0 | "Garmin: SAT_DATA: visible=%d used=%d\n", |
607 | 0 | session->gpsdata.satellites_visible, |
608 | 0 | session->gpsdata.satellites_used); |
609 | 0 | break; |
610 | 0 | case GARMIN_PKTID_PROTOCOL_ARRAY: |
611 | | // Pid_Protocol_Array, ID 253 |
612 | | // this packet is never requested, it just comes, in some case |
613 | | // after a GARMIN_PKTID_PRODUCT_RQST |
614 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
615 | 0 | "Garmin: Appl, Product Capability, sz: %d\n", |
616 | 0 | pkt_len); |
617 | 0 | for (i = 0; i < pkt_len; i += 3) { |
618 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, |
619 | 0 | "Garmin: %c%03d\n", |
620 | 0 | buf[i], get_uint16((uint8_t *) & buf[i + 1])); |
621 | 0 | } |
622 | 0 | break; |
623 | 0 | default: |
624 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
625 | 0 | "Garmin: Unknown packet id: %#02x, Sz: %#02x\n", |
626 | 0 | pkt_id, pkt_len); |
627 | 0 | break; |
628 | 0 | } |
629 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
630 | 0 | "Garmin: PrintSERPacket(, %#02x, %#02x, ) mask=(%s)\n", |
631 | 0 | pkt_id, pkt_len, gps_maskdump(mask)); |
632 | 0 | return mask; |
633 | 0 | } |
634 | | |
635 | | |
636 | | #if defined(HAVE_LIBUSB) && defined(__linux__) |
637 | | // For debugging, decodes and prints some known packets |
638 | | static gps_mask_t PrintUSBPacket(struct gps_device_t *session, Packet_t * pkt) |
639 | | { |
640 | | gps_mask_t mask = 0; |
641 | | int maj_ver; |
642 | | int min_ver; |
643 | | uint32_t mode = 0; |
644 | | uint16_t prod_id = 0; |
645 | | uint32_t veri = 0; |
646 | | uint32_t serial; |
647 | | uint32_t mDataSize = get_int32((uint8_t *) & pkt->mDataSize); |
648 | | uint8_t *buffer = (uint8_t *) pkt; |
649 | | |
650 | | GPSD_LOG(LOG_PROG, &session->context->errout, "Garmin: PrintUSBPacket()\n"); |
651 | | if (DLE == pkt->mPacketType) { |
652 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
653 | | "Garmin: really a SER packet!\n"); |
654 | | return PrintSERPacket(session, |
655 | | (unsigned char)buffer[1], |
656 | | (int)buffer[2], (unsigned char *)(buffer + 3)); |
657 | | } |
658 | | |
659 | | if (4096 < mDataSize) { |
660 | | GPSD_LOG(LOG_WARN, &session->context->errout, |
661 | | "Garmin: bogus packet, size too large=%d\n", |
662 | | mDataSize); |
663 | | return 0; |
664 | | } |
665 | | |
666 | | switch (pkt->mPacketType) { |
667 | | case GARMIN_LAYERID_TRANSPORT: |
668 | | // Garmin USB layer specific |
669 | | switch (pkt->mPacketId) { |
670 | | case GARMIN_PKTID_TRANSPORT_START_SESSION_REQ: |
671 | | // Pid_Start_Session, ID 5 |
672 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
673 | | "Garmin: Transport, Start Session req\n"); |
674 | | break; |
675 | | case GARMIN_PKTID_TRANSPORT_START_SESSION_RESP: |
676 | | // Pid_Session_Started, ID 6 |
677 | | mode = get_int32(&pkt->mData.uchars[0]); |
678 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
679 | | "Garmin: Transport, Start Session resp, unit: 0x%x\n", |
680 | | mode); |
681 | | break; |
682 | | default: |
683 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
684 | | "Garmin: Transport, Packet: Type %d %d %d, ID: %d," |
685 | | "Sz: %d\n", |
686 | | pkt->mPacketType, pkt->mReserved1, pkt->mReserved2, |
687 | | pkt->mPacketId, mDataSize); |
688 | | break; |
689 | | } |
690 | | break; |
691 | | case GARMIN_LAYERID_APPL: |
692 | | // raw data transport, shared with Garmin Serial Driver |
693 | | |
694 | | mask = PrintSERPacket(session, |
695 | | (unsigned char)pkt->mPacketId, |
696 | | (int)mDataSize, |
697 | | (unsigned char *)pkt->mData.uchars); |
698 | | break; |
699 | | case 75: |
700 | | // private, garmin USB kernel driver specific |
701 | | switch (pkt->mPacketId) { |
702 | | case PRIV_PKTID_SET_MODE: |
703 | | prod_id = get_uint16(&pkt->mData.uchars[0]); |
704 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
705 | | "Garmin: Private, Set Mode: %d\n", prod_id); |
706 | | break; |
707 | | case PRIV_PKTID_INFO_REQ: |
708 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
709 | | "Garmin: Private, ID: Info Req\n"); |
710 | | break; |
711 | | case PRIV_PKTID_INFO_RESP: |
712 | | veri = get_int32(pkt->mData.uchars); |
713 | | maj_ver = (int)(veri >> 16); |
714 | | min_ver = (int)(veri & 0xffff); |
715 | | mode = get_int32(&pkt->mData.uchars[4]); |
716 | | serial = get_int32(&pkt->mData.uchars[8]); |
717 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
718 | | "Garmin: Private, ID: Info Resp\n"); |
719 | | GPSD_LOG(LOG_INF, &session->context->errout, |
720 | | "Garmin: USB Driver Version %d.%d Mode %d, Serial# %u\n", |
721 | | maj_ver, min_ver, mode, serial); |
722 | | break; |
723 | | default: |
724 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
725 | | "Garmin: Private, Packet: ID: %d, Sz: %d\n", |
726 | | pkt->mPacketId, mDataSize); |
727 | | break; |
728 | | } |
729 | | break; |
730 | | default: |
731 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
732 | | "Garmin: Packet: Type %d %d %d, ID: %d, Sz: %d\n", |
733 | | pkt->mPacketType, pkt->mReserved1, pkt->mReserved2, |
734 | | pkt->mPacketId, mDataSize); |
735 | | break; |
736 | | } |
737 | | |
738 | | return mask; |
739 | | } |
740 | | |
741 | | #endif // HAVE_LIBUSB |
742 | | |
743 | | |
744 | | #if defined(HAVE_LIBUSB) && defined(__linux__) |
745 | | // build and send a packet w/ USB protocol |
746 | | static void Build_Send_USB_Packet(struct gps_device_t *session, |
747 | | uint32_t layer_id, uint32_t pkt_id, |
748 | | uint32_t length, uint32_t data) |
749 | | { |
750 | | uint8_t *buffer = (uint8_t *) session->driver.garmin.Buffer; |
751 | | Packet_t *thePacket = (Packet_t *) buffer; |
752 | | ssize_t theBytesReturned = 0; |
753 | | ssize_t theBytesToWrite = 12 + (ssize_t) length; |
754 | | |
755 | | set_int32(buffer, layer_id); |
756 | | set_int32(buffer + 4, pkt_id); |
757 | | set_int32(buffer + 8, length); |
758 | | if (2 == length) { |
759 | | set_int16(buffer + 12, data); |
760 | | } else if (4 == length) { |
761 | | set_int32(buffer + 12, data); |
762 | | } |
763 | | (void)PrintUSBPacket(session, thePacket); |
764 | | |
765 | | theBytesReturned = gpsd_write(session, (const char *)thePacket, |
766 | | (size_t) theBytesToWrite); |
767 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
768 | | "Garmin: SendPacket(), wrote %zd bytes\n", |
769 | | theBytesReturned); |
770 | | |
771 | | // Garmin says: |
772 | | // If the packet size was an exact multiple of the USB packet |
773 | | // size, we must make a final write call with no data |
774 | | |
775 | | // as a practical matter no known packets are 64 bytes long so |
776 | | // this is untested |
777 | | |
778 | | // So here goes just in case |
779 | | if (0 == (theBytesToWrite % ASYNC_DATA_SIZE)) { |
780 | | char *n = ""; |
781 | | (void)gpsd_write(session, n, 0); |
782 | | } |
783 | | } |
784 | | #endif // HAVE_LIBUSB && __linux__ |
785 | | |
786 | | /* build and send a packet in serial protocol |
787 | | * layer_id unused */ |
788 | | // FIX-ME: This should go through the common message buffer someday |
789 | | static void Build_Send_SER_Packet(struct gps_device_t *session, |
790 | | uint32_t layer_id UNUSED, uint32_t pkt_id, |
791 | | uint32_t length, uint32_t data) |
792 | 0 | { |
793 | 0 | uint8_t *buffer = (uint8_t *) session->driver.garmin.Buffer; |
794 | 0 | uint8_t *buffer0 = buffer; |
795 | 0 | Packet_t *thePacket = (Packet_t *) buffer; |
796 | 0 | ssize_t theBytesReturned = 0; |
797 | 0 | ssize_t theBytesToWrite = 6 + (ssize_t) length; |
798 | 0 | uint8_t chksum = 0; |
799 | |
|
800 | 0 | *buffer++ = (uint8_t) DLE; |
801 | 0 | *buffer++ = (uint8_t) pkt_id; |
802 | 0 | chksum = pkt_id; |
803 | 0 | *buffer++ = (uint8_t) length; |
804 | 0 | chksum += length; |
805 | | // ??? What is this doing? |
806 | 0 | if (2 == length) { |
807 | | // careful! no DLE stuffing here! |
808 | 0 | set_int16(buffer, data); |
809 | 0 | chksum += buffer[0]; |
810 | 0 | chksum += buffer[1]; |
811 | 0 | } else if (4 == length) { |
812 | | // careful! no DLE stuffing here! |
813 | 0 | set_int32(buffer, data); |
814 | 0 | chksum += buffer[0]; |
815 | 0 | chksum += buffer[1]; |
816 | 0 | chksum += buffer[2]; |
817 | 0 | chksum += buffer[3]; |
818 | 0 | } |
819 | | // ??? How is data copied to the buffer? |
820 | 0 | buffer += length; |
821 | | |
822 | | // Add checksum |
823 | 0 | *buffer++ = -chksum; |
824 | 0 | if (DLE == -chksum) { |
825 | | // stuff another DLE |
826 | 0 | *buffer++ = (uint8_t) DLE; |
827 | 0 | theBytesToWrite++; |
828 | 0 | } |
829 | | // Add DLE, ETX |
830 | 0 | *buffer++ = (uint8_t) DLE; |
831 | | // we used to say n++ here, but scan-build complains |
832 | 0 | *buffer = (uint8_t) ETX; |
833 | |
|
834 | 0 | (void)PrintSERPacket(session, |
835 | 0 | (unsigned char)buffer0[1], |
836 | 0 | (int)buffer0[2], (unsigned char *)(buffer0 + 3)); |
837 | |
|
838 | 0 | theBytesReturned = gpsd_write(session, (const char *)thePacket, |
839 | 0 | (size_t) theBytesToWrite); |
840 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
841 | 0 | "Garmin: SendPacket(), wrote %zd bytes\n", |
842 | 0 | theBytesReturned); |
843 | |
|
844 | 0 | } |
845 | | |
846 | | #if defined(HAVE_LIBUSB) && defined(__linux__) |
847 | | /* |
848 | | * is_usb_device() - is a specified device USB matching given vendor/product? |
849 | | * |
850 | | * BUG: Doesn't actually match against path yet. Must finish this function |
851 | | * by querying /sys/dev/char, either directly or using libudev. Greg KH |
852 | | * assures this is possible, though he is vague about how. |
853 | | * |
854 | | * libudev: http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/ |
855 | | */ |
856 | | static bool is_usb_device(const char *path UNUSED, int vendor, int product, |
857 | | struct gpsd_errout_t *errout) |
858 | | { |
859 | | // discover devices |
860 | | libusb_device **list; |
861 | | ssize_t cnt; |
862 | | ssize_t i = 0; |
863 | | bool found = false; |
864 | | |
865 | | GPSD_LOG(LOG_INF, errout, "attempting USB device enumeration.\n"); |
866 | | (void)libusb_init(NULL); |
867 | | |
868 | | if (0 > (cnt = libusb_get_device_list(NULL, &list))) { |
869 | | GPSD_LOG(LOG_ERROR, errout, "USB device list call failed.\n"); |
870 | | libusb_exit(NULL); |
871 | | return false; |
872 | | } |
873 | | |
874 | | for (i = 0; i < cnt; i++) { |
875 | | struct libusb_device_descriptor desc; |
876 | | libusb_device *dev = list[i]; |
877 | | |
878 | | int r = libusb_get_device_descriptor(dev, &desc); |
879 | | if (0 > r) { |
880 | | GPSD_LOG(LOG_ERROR, errout, |
881 | | "USB descriptor fetch failed on device %zd.\n", i); |
882 | | continue; |
883 | | } |
884 | | |
885 | | // we can extract device descriptor data |
886 | | GPSD_LOG(LOG_INF, errout, |
887 | | "%04x:%04x (bus %d, device %d)\n", |
888 | | desc.idVendor, desc.idProduct, |
889 | | libusb_get_bus_number(dev), |
890 | | libusb_get_device_address(dev)); |
891 | | |
892 | | // we match if vendor and product ID are right |
893 | | if (desc.idVendor == (uint16_t)vendor && |
894 | | desc.idProduct == (uint16_t)product) { |
895 | | found = true; |
896 | | break; |
897 | | } |
898 | | } |
899 | | |
900 | | GPSD_LOG(LOG_INF, errout, |
901 | | "vendor/product match with %04x:%04x %sfound\n", |
902 | | vendor, product, found ? "" : "not "); |
903 | | libusb_free_device_list(list, 1); |
904 | | libusb_exit(NULL); |
905 | | return found; |
906 | | } |
907 | | |
908 | | #endif // HAVE_LIBUSB |
909 | | |
910 | | /* |
911 | | * garmin_usb_detect() - detect a Garmin USB device connected to session fd. |
912 | | * |
913 | | * This is ONLY for USB devices reporting as: 091e:0003. |
914 | | * |
915 | | * This driver ONLY works in Linux and ONLY when the garmin_gps kernel |
916 | | * module is installed. |
917 | | * |
918 | | * This is only necessary because under Linux Garmin USB devices need a |
919 | | * kernel module rather than being normal USB-serial devices. |
920 | | * |
921 | | * The actual wire protocol from the Garmin device is very strange. There |
922 | | * are no delimiters. End of packet is signaled by a zero-length read |
923 | | * on the USB device, and start of packet is the next read. You can't just |
924 | | * ignore the zero reads and pass the data through - you'd never be able |
925 | | * to tell where the packet boundaries are. |
926 | | * |
927 | | * The garmin_usb module's job is to grab the packet and frame it in |
928 | | * DLEs (with DLE stuffing). This makes the USB packets look as |
929 | | * though they came from a regular Garmin *serial* device, which is how |
930 | | * most of the processing for both types can be unified here. |
931 | | * |
932 | | * return 1 is device found |
933 | | * return 0 if not |
934 | | */ |
935 | | static bool garmin_usb_detect(struct gps_device_t *session UNUSED) |
936 | 0 | { |
937 | 0 | #if defined(__linux__) |
938 | | /* |
939 | | * Only perform this check if we're looking at a USB-serial |
940 | | * device. This prevents drivers for attached serial GPSes |
941 | | * fronm being rudely elbowed aside by this one if they happen |
942 | | * to be trying to coexist with the Garmin. |
943 | | */ |
944 | 0 | if (SOURCE_USB != session->sourcetype) { |
945 | 0 | return false; |
946 | 0 | } |
947 | | |
948 | | #ifdef HAVE_LIBUSB |
949 | | if (!is_usb_device(session->gpsdata.dev.path, 0x091e, 0x0003, |
950 | | &session->context->errout)) { |
951 | | return false; |
952 | | } |
953 | | |
954 | | if (!gpsd_set_raw(session)) { |
955 | | GPSD_LOG(LOG_ERROR, &session->context->errout, |
956 | | "Garmin: garmin_usb_detect: error changing port " |
957 | | "attributes: %s\n", |
958 | | strerror(errno)); |
959 | | return false; |
960 | | } |
961 | | |
962 | | if (sizeof(session->driver.garmin.Buffer) < sizeof(Packet_t)) { |
963 | | // dunno how this happens, but it does on some compilers |
964 | | GPSD_LOG(LOG_ERROR, &session->context->errout, |
965 | | "Garmin: garmin_usb_detect: Compile error, " |
966 | | "garmin.Buffer too small.\n"); |
967 | | return false; |
968 | | } |
969 | | |
970 | | // FIXME!!! needs to use libusb totally and move garmin_gps aside */ |
971 | | // set Mode 1, mode 0 is broken somewhere past 2.6.14 |
972 | | // but how? |
973 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
974 | | "Garmin: Set garmin_gps driver mode = 0\n"); |
975 | | Build_Send_USB_Packet(session, GARMIN_LAYERID_PRIVATE, |
976 | | PRIV_PKTID_SET_MODE, 4, MODE_GARMIN_SERIAL); |
977 | | // expect no return packet !? |
978 | | |
979 | | return true; |
980 | | #endif // HAVE_LIBUSB |
981 | 0 | #endif // __linux__ |
982 | 0 | return false; |
983 | 0 | } |
984 | | |
985 | | static void garmin_event_hook(struct gps_device_t *session, event_t event) |
986 | 0 | { |
987 | 0 | if (session->context->readonly || |
988 | 0 | session->context->passive) { |
989 | 0 | return; |
990 | 0 | } |
991 | | /* |
992 | | * FIX-ME: It might not be necessary to call this on reactivate. |
993 | | * Experiment to see if the holds its settings through a close. |
994 | | */ |
995 | 0 | if (event == event_identified || event == event_reactivate) { |
996 | | // Tell the device to send product data |
997 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
998 | 0 | "Garmin: Get Product Data\n"); |
999 | 0 | Build_Send_SER_Packet(session, GARMIN_LAYERID_APPL, |
1000 | 0 | GARMIN_PKTID_PRODUCT_RQST, 0, 0); |
1001 | | |
1002 | | // turn on PVT data 49 |
1003 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1004 | 0 | "Garmin: Set to send reports every 1 second\n"); |
1005 | |
|
1006 | 0 | Build_Send_SER_Packet(session, GARMIN_LAYERID_APPL, |
1007 | 0 | GARMIN_PKTID_L001_COMMAND_DATA, 2, |
1008 | 0 | CMND_START_PVT_DATA); |
1009 | |
|
1010 | | #if USE_RMD |
1011 | | // turn on RMD data 110 |
1012 | | GPSD_LOG(LOG_PROG, &session->context->errout, |
1013 | | "Garmin: Set to send Raw sat data\n"); |
1014 | | Build_Send_SER_Packet(session, GARMIN_LAYERID_APPL, |
1015 | | GARMIN_PKTID_L001_COMMAND_DATA, 2, |
1016 | | CMND_START_RM_DATA); |
1017 | | #endif |
1018 | 0 | } |
1019 | 0 | if (event == event_deactivate) |
1020 | | // FIX-ME: is any action needed, or is closing the port sufficient? |
1021 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1022 | 0 | "Garmin: garmin_close()\n"); |
1023 | 0 | } |
1024 | | |
1025 | 0 | #define Send_ACK() Build_Send_SER_Packet(session, 0, ACK, 0, 0) |
1026 | 0 | #define Send_NAK() Build_Send_SER_Packet(session, 0, NAK, 0, 0) |
1027 | | |
1028 | | gps_mask_t garmin_ser_parse(struct gps_device_t *session) |
1029 | 0 | { |
1030 | 0 | unsigned char *buf = session->lexer.outbuffer; |
1031 | 0 | size_t len = session->lexer.outbuflen; |
1032 | 0 | unsigned char data_buf[MAX_BUFFER_SIZE]; |
1033 | 0 | unsigned char c; |
1034 | 0 | int i = 0; |
1035 | 0 | size_t n = 0; |
1036 | 0 | int data_index = 0; |
1037 | 0 | int got_dle = 0; |
1038 | 0 | unsigned char pkt_id = 0; |
1039 | 0 | unsigned char pkt_len = 0; |
1040 | 0 | unsigned char chksum = 0; |
1041 | 0 | gps_mask_t mask = 0; |
1042 | 0 | struct timespec delay; |
1043 | |
|
1044 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1045 | 0 | "Garmin: garmin_ser_parse()\n"); |
1046 | 0 | if (6 > len) { |
1047 | | // WTF? |
1048 | | // minimum packet; <DLE> [pkt id] [length=0] [chksum] <DLE> <STX> |
1049 | 0 | Send_NAK(); |
1050 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1051 | 0 | "Garmin: serial too short: %zd\n", len); |
1052 | 0 | return 0; |
1053 | 0 | } |
1054 | | // debug |
1055 | 0 | for (i = 0; i < (int)len; i++) { |
1056 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1057 | 0 | "Garmin: Char: %#02x\n", buf[i]); |
1058 | 0 | } |
1059 | |
|
1060 | 0 | if ('\x10' != buf[0]) { |
1061 | 0 | Send_NAK(); |
1062 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1063 | 0 | "Garmin: buf[0] not DLE\n"); |
1064 | 0 | return 0; |
1065 | 0 | } |
1066 | 0 | n = 1; |
1067 | 0 | pkt_id = buf[n++]; |
1068 | 0 | chksum = pkt_id; |
1069 | 0 | if ('\x10' == pkt_id) { |
1070 | 0 | if ('\x10' != buf[n++]) { |
1071 | 0 | Send_NAK(); |
1072 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1073 | 0 | "Garmin: Bad pkt_id %#02x\n", pkt_id); |
1074 | 0 | return 0; |
1075 | 0 | } |
1076 | 0 | } |
1077 | | |
1078 | 0 | pkt_len = buf[n++]; |
1079 | 0 | chksum += pkt_len; |
1080 | 0 | if ('\x10' == pkt_len) { |
1081 | 0 | if ('\x10' != buf[n++]) { |
1082 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1083 | 0 | "Garmin: Bad pkt_len %#02x\n", pkt_len); |
1084 | 0 | Send_NAK(); |
1085 | 0 | return 0; |
1086 | 0 | } |
1087 | 0 | } |
1088 | 0 | data_index = 0; |
1089 | 0 | for (i = 0; i < 256; i++) { |
1090 | |
|
1091 | 0 | if ((int)pkt_len == data_index) { |
1092 | | // got it all |
1093 | 0 | break; |
1094 | 0 | } |
1095 | 0 | if (len < n + i) { |
1096 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1097 | 0 | "Garmin: Packet too short %zd < %zd\n", |
1098 | 0 | len, n + i); |
1099 | 0 | Send_NAK(); |
1100 | 0 | return 0; |
1101 | 0 | } |
1102 | 0 | c = buf[n + i]; |
1103 | 0 | if (got_dle) { |
1104 | 0 | got_dle = 0; |
1105 | 0 | if ('\x10' != c) { |
1106 | 0 | Send_NAK(); |
1107 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1108 | 0 | "Garmin: Bad DLE %#02x\n", c); |
1109 | 0 | return 0; |
1110 | 0 | } |
1111 | 0 | } else { |
1112 | 0 | chksum += c; |
1113 | 0 | data_buf[data_index++] = c; |
1114 | 0 | if ('\x10' == c) { |
1115 | 0 | got_dle = 1; |
1116 | 0 | } |
1117 | 0 | } |
1118 | 0 | } |
1119 | | // get checksum |
1120 | 0 | if (len < n + i) { |
1121 | 0 | Send_NAK(); |
1122 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1123 | 0 | "Garmin: No checksum, Packet too short %zd < %zd\n", len, |
1124 | 0 | n + i); |
1125 | 0 | return 0; |
1126 | 0 | } |
1127 | 0 | c = buf[n + i++]; |
1128 | 0 | chksum += c; |
1129 | | // get final DLE |
1130 | 0 | if (len < n + i) { |
1131 | 0 | Send_NAK(); |
1132 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1133 | 0 | "Garmin: No final DLE, Packet too short %zd < %zd\n", len, |
1134 | 0 | n + i); |
1135 | 0 | return 0; |
1136 | 0 | } |
1137 | 0 | c = buf[n + i++]; |
1138 | 0 | if ('\x10' != c) { |
1139 | 0 | Send_NAK(); |
1140 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1141 | 0 | "Garmin: Final DLE not DLE\n"); |
1142 | 0 | return 0; |
1143 | 0 | } |
1144 | | // get final ETX |
1145 | 0 | if (len < n + i) { |
1146 | 0 | Send_NAK(); |
1147 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1148 | 0 | "Garmin: No final ETX, Packet too short %zd < %zd\n", len, |
1149 | 0 | n + i); |
1150 | 0 | return 0; |
1151 | 0 | } |
1152 | | // we used to say n++ here, but scan-build complains |
1153 | 0 | c = buf[n + i]; |
1154 | 0 | if ('\x03' != c) { |
1155 | 0 | Send_NAK(); |
1156 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1157 | 0 | "Garmin: Final ETX not ETX\n"); |
1158 | 0 | return 0; |
1159 | 0 | } |
1160 | | |
1161 | | /* debug */ |
1162 | 0 | for (i = 0; i < data_index; i++) { |
1163 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
1164 | 0 | "Garmin: Char %#02x\n", data_buf[i]); |
1165 | 0 | } |
1166 | | |
1167 | |
|
1168 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
1169 | 0 | "Garmin: garmin_ser_parse() Type %#02x Len %#02x chksum %#02x\n", |
1170 | 0 | pkt_id, pkt_len, chksum); |
1171 | 0 | mask = PrintSERPacket(session, pkt_id, pkt_len, data_buf); |
1172 | | |
1173 | | /* sending ACK too soon might hang the session |
1174 | | * so send ACK last, after a pause, then wait 300 uSec */ |
1175 | 0 | delay.tv_sec = 0; |
1176 | 0 | delay.tv_nsec = 300000L; |
1177 | 0 | nanosleep(&delay, NULL); |
1178 | |
|
1179 | 0 | Send_ACK(); |
1180 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
1181 | 0 | "Garmin: garmin_ser_parse( )\n"); |
1182 | 0 | return mask; |
1183 | 0 | } |
1184 | | |
1185 | | |
1186 | | static void settle(void) |
1187 | 0 | { |
1188 | 0 | struct timespec delay, rem; |
1189 | 0 | memset(&delay, 0, sizeof(delay)); |
1190 | 0 | delay.tv_sec = 0; |
1191 | 0 | delay.tv_nsec = 333000000L; |
1192 | 0 | nanosleep(&delay, &rem); |
1193 | 0 | } |
1194 | | |
1195 | | static void garmin_switcher(struct gps_device_t *session, int mode) |
1196 | 0 | { |
1197 | 0 | if (mode == MODE_NMEA) { |
1198 | 0 | const char switcher[] = |
1199 | 0 | { 0x10, 0x0A, 0x02, 0x26, 0x00, 0xCE, 0x10, 0x03 }; |
1200 | | // Note hard-coded string length in the next line... |
1201 | 0 | ssize_t status = gpsd_write(session, switcher, sizeof(switcher)); |
1202 | 0 | if ((ssize_t)sizeof(switcher) == status) { |
1203 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1204 | 0 | "Garmin: => GPS: turn off binary %02x %02x %02x... \n", |
1205 | 0 | switcher[0], switcher[1], switcher[2]); |
1206 | 0 | } else { |
1207 | 0 | GPSD_LOG(LOG_ERROR, &session->context->errout, |
1208 | 0 | "Garmin: => GPS: FAILED\n"); |
1209 | 0 | } |
1210 | 0 | settle(); // wait 333mS, essential! |
1211 | | |
1212 | | // once a sec, no binary, no averaging, NMEA 2.3, WAAS |
1213 | 0 | (void)nmea_send(session, "$PGRMC1,1,1"); |
1214 | | //(void)nmea_send(fd, "$PGRMC1,1,1,1,,,,2,W,N"); |
1215 | 0 | (void)nmea_send(session, "$PGRMI,,,,,,,R"); |
1216 | 0 | settle(); // wait 333mS, essential! |
1217 | 0 | } else { |
1218 | 0 | (void)nmea_send(session, "$PGRMC1,1,2,1,,,,2,W,N"); |
1219 | 0 | (void)nmea_send(session, "$PGRMI,,,,,,,R"); |
1220 | 0 | settle(); // wait 333mS, essential! |
1221 | 0 | } |
1222 | 0 | } |
1223 | | |
1224 | | // not used by the daemon, it's for gpsctl and friends |
1225 | | static ssize_t garmin_control_send(struct gps_device_t *session, |
1226 | | char *buf, size_t buflen) |
1227 | 0 | { |
1228 | 0 | session->msgbuflen = buflen; |
1229 | 0 | (void)memcpy(session->msgbuf, buf, buflen); |
1230 | 0 | return gpsd_write(session, session->msgbuf, session->msgbuflen); |
1231 | 0 | } |
1232 | | |
1233 | | static double garmin_time_offset(struct gps_device_t *session) |
1234 | 0 | { |
1235 | 0 | if (SOURCE_USB == session->sourcetype) { |
1236 | 0 | return 0.035; // Garmin USB, expect +/- 40mS jitter |
1237 | 0 | } |
1238 | | /* only two sentences ships time |
1239 | | * but the PVT data is always first */ |
1240 | 0 | switch (session->gpsdata.dev.baudrate) { |
1241 | 0 | case 4800: |
1242 | 0 | return 0.430; // TBD |
1243 | 0 | case 9600: |
1244 | 0 | return 0.430; // tested 12Arp10 |
1245 | 0 | case 19200: |
1246 | 0 | return 0.430; // TBD |
1247 | 0 | case 38400: |
1248 | 0 | return 0.430; // TBD |
1249 | 0 | } |
1250 | 0 | return 0.430; // WTF? WAG |
1251 | 0 | } |
1252 | | |
1253 | | // this is everything we export |
1254 | | |
1255 | | // *INDENT-OFF* |
1256 | | const struct gps_type_t driver_garmin_usb_binary = |
1257 | | { |
1258 | | .type_name = "Garmin USB binary", // full name of type |
1259 | | .packet_type = GARMIN_PACKET, // associated lexer packet type |
1260 | | .flags = DRIVER_STICKY, // remember this |
1261 | | .trigger = NULL, // no trigger, it has a probe |
1262 | | .channels = GARMIN_CHANNELS, // consumer-grade GPS |
1263 | | .probe_detect = garmin_usb_detect, // how to detect at startup time |
1264 | | .get_packet = packet_get1, // how to grab a packet |
1265 | | .parse_packet = garmin_ser_parse, // parse message packets |
1266 | | .rtcm_writer = NULL, // don't send DGPS corrections |
1267 | | .init_query = NULL, // non-perturbing initial query |
1268 | | .event_hook = garmin_event_hook, // lifetime ebent handler |
1269 | | .speed_switcher = NULL, // no speed switcher |
1270 | | .mode_switcher = NULL, // Garmin USB Binary has no NMEA |
1271 | | .rate_switcher = NULL, // no sample-rate switcher |
1272 | | .min_cycle.tv_sec = 0, |
1273 | | .min_cycle.tv_nsec = 10000000, // 10Hz |
1274 | | .control_send = garmin_control_send, // send raw bytes |
1275 | | .time_offset = garmin_time_offset, |
1276 | | }; |
1277 | | |
1278 | | const struct gps_type_t driver_garmin_ser_binary = |
1279 | | { |
1280 | | .type_name = "Garmin Serial binary", // full name of type |
1281 | | .packet_type = GARMIN_PACKET, // associated lexer packet type |
1282 | | .flags = DRIVER_STICKY, // remember this |
1283 | | .trigger = NULL, // no trigger, it has a probe |
1284 | | .channels = GARMIN_CHANNELS, // consumer-grade GPS |
1285 | | .probe_detect = NULL, // how to detect at startup time |
1286 | | .get_packet = packet_get1, // how to grab a packet |
1287 | | .parse_packet = garmin_ser_parse, // parse message packets |
1288 | | .rtcm_writer = NULL, // don't send DGPS corrections |
1289 | | .init_query = NULL, // non-perturbing initial query |
1290 | | /* The Garmin Gek0 301 needs to be kicked to start sending binary. |
1291 | | * like the Garmin USB. */ |
1292 | | .event_hook = garmin_event_hook, // lifetime event handler |
1293 | | .speed_switcher = NULL, // no speed switcher |
1294 | | .mode_switcher = garmin_switcher, // how to change modes |
1295 | | .rate_switcher = NULL, // no sample-rate switcher |
1296 | | .min_cycle.tv_sec = 0, |
1297 | | .min_cycle.tv_nsec = 10000000, // 10Hz |
1298 | | .control_send = garmin_control_send, // send raw bytes |
1299 | | .time_offset = garmin_time_offset, |
1300 | | }; |
1301 | | // *INDENT-ON* |
1302 | | |
1303 | | #endif /* GARMIN_ENABLE */ |
1304 | | |
1305 | | // vim: set expandtab shiftwidth=4 |