1 /*
2  * A prototype driver.  Doesn't run, doesn't even compile.
3  *
4  * For new driver authors: replace "_PROTO_" and "_proto_" with the name of
5  * your new driver. That will give you a skeleton with all the required
6  * functions defined.
7  *
8  * Once that is done, you will likely have to define a large number of
9  * flags and masks. From there, you will be able to start extracting
10  * useful quantities. There are roughed-in decoders for the navigation
11  * solution, satellite status and gps-utc offset. These are the 3 key
12  * messages that gpsd needs. Some protocols transmit error estimates
13  * separately from the navigation solution; if developing a driver for
14  * such a protocol you will need to add a decoder function for that
15  * message. Be extra careful when using sizeof(<type>) to extract part
16  * of packets (ie. don't do it). This idiom creates portability problems
17  * between 32 and 64 bit systems.
18  *
19  * For anyone hacking this driver skeleton: "_PROTO_" and "_proto_" are now
20  * reserved tokens. We suggest that they only ever be used as prefixes,
21  * but if they are used infix, they must be used in a way that allows a
22  * driver author to find-and-replace to create a unique namespace for
23  * driver functions.
24  *
25  * If using vi, ":%s/_PROTO_/MYDRIVER/g" and ":%s/_proto_/mydriver/g"
26  * should produce a source file that comes very close to being useful.
27  * You will also need to add hooks for your new driver to:
28  * SConstruct
29  * drivers.c
30  * gpsd.h-tail
31  * libgpsd_core.c
32  * packet.c
33  * packet_states.h
34  *
35  * This file is Copyright (c) 2010-2018 by the GPSD project
36  * SPDX-License-Identifier: BSD-2-clause
37  */
38 
39 #include "gpsd_config.h"  /* must be before all includes */
40 
41 #include <stdio.h>
42 #include <stdbool.h>
43 #include <string.h>
44 
45 #if defined(_PROTO__ENABLE) && defined(BINARY_ENABLE)
46 
47 #include "bits.h"
48 
49 static	gps_mask_t _proto__parse_input(struct gps_device_t *);
50 static	gps_mask_t _proto__dispatch(struct gps_device_t *, unsigned char *, size_t );
51 static	gps_mask_t _proto__msg_navsol(struct gps_device_t *, unsigned char *, size_t );
52 static	gps_mask_t _proto__msg_utctime(struct gps_device_t *, unsigned char *, size_t );
53 static	gps_mask_t _proto__msg_svinfo(struct gps_device_t *, unsigned char *, size_t );
54 static	gps_mask_t _proto__msg_raw(struct gps_device_t *, unsigned char *, size_t );
55 
56 /*
57  * These methods may be called elsewhere in gpsd
58  */
59 static	ssize_t _proto__control_send(struct gps_device_t *, char *, size_t);
60 static	bool _proto__probe_detect(struct gps_device_t *);
61 static	void _proto__event_hook(struct gps_device_t *, event_t);
62 static	bool _proto__set_speed(struct gps_device_t *, speed_t, char, int);
63 static	void _proto__set_mode(struct gps_device_t *, int);
64 
65 /*
66  * Decode the navigation solution message
67  */
68 static gps_mask_t
_proto__msg_navsol(struct gps_device_t * session,unsigned char * buf,size_t data_len)69 _proto__msg_navsol(struct gps_device_t *session, unsigned char *buf, size_t data_len)
70 {
71     gps_mask_t mask;
72     int flags;
73     double Px, Py, Pz, Vx, Vy, Vz;
74 
75     if (data_len != _PROTO__NAVSOL_MSG_LEN)
76 	return 0;
77 
78     GPSD_LOG(LOG_DATA, &session->context->errout,
79 	     "_proto_ NAVSOL - navigation data\n");
80     /* if this protocol has a way to test message validity, use it */
81     flags = GET_FLAGS();
82     if ((flags & _PROTO__SOLUTION_VALID) == 0)
83 	return 0;
84 
85     mask = ONLINE_SET;
86 
87     /* extract ECEF navigation solution here */
88     /* or extract the local tangential plane (ENU) solution */
89     [session->newdata.ecef.x,
90     session->newdata.ecef.y,
91     session->newdata.ecef.z,
92     session->newdata.ecef.vx,
93     session->newdata.ecef.vy,
94     session->newdata.ecef.vz] = GET_ECEF_FIX();
95     mask |= ECEF_SET | VECEF_SET;
96 
97     session->newdata.epx = GET_LONGITUDE_ERROR();
98     session->newdata.epy = GET_LATITUDE_ERROR();
99     session->newdata.eps = GET_SPEED_ERROR();
100     session->gpsdata.satellites_used = GET_SATELLITES_USED();
101     /*
102      * Do *not* clear DOPs in a navigation solution message;
103      * instead, opportunistically pick up whatever it gives
104      * us and replace whatever values we computed from the
105      * visibility matrix for he last skyview. The reason to trust
106      * the chip returns over what we compute is that some
107      * chips have internal deweighting albums to throw out sats
108      * that increase DOP.
109      */
110     session->gpsdata.dop.hdop = GET_HDOP();
111     session->gpsdata.dop.vdop = GET_VDOP();
112     /* other DOP if available */
113     mask |= DOP_SET;
114 
115     session->newdata.mode = GET_FIX_MODE();
116     session->gpsdata.status = GET_FIX_STATUS();
117 
118     /*
119      * Mix in CLEAR_IS to clue the daemon in about when to clear fix
120      * information.  Mix in REPORT_IS when the sentence is reliably
121      * the last in a reporting cycle.
122      */
123     mask |= MODE_SET | STATUS_SET | REPORT_IS;
124 
125     /*
126      * At the end of each packet-cracking function, report at LOG_DATA level
127      * the fields it potentially set and the transfer mask. Doing this
128      * makes it relatively easy to track down data-management problems.
129      */
130     GPSD_LOG(LOG_DATA, &session->context->errout,
131 	     "NAVSOL: time=%.2f, ecef x:%.2f y: %.2f z: %.2f mode=%d "
132              "status=%d\n",
133 	     session->newdata.time,
134 	     session->newdata.ecef.x,
135 	     session->newdata.ecef.y,
136 	     session->newdata.ecef.z,
137 	     session->newdata.longitude,
138 	     session->newdata.mode,
139 	     session->gpsdata.status);
140 
141     return mask;
142 }
143 
144 /**
145  * GPS Leap Seconds
146  */
147 static gps_mask_t
_proto__msg_utctime(struct gps_device_t * session,unsigned char * buf,size_t data_len)148 _proto__msg_utctime(struct gps_device_t *session, unsigned char *buf, size_t data_len)
149 {
150     double t;
151 
152     if (data_len != UTCTIME_MSG_LEN)
153 	return 0;
154 
155     GPSD_LOG(LOG_DATA, &session->context->errout,
156 	     "_proto_ UTCTIME - navigation data\n");
157     /* if this protocol has a way to test message validity, use it */
158     flags = GET_FLAGS();
159     if ((flags & _PROTO__TIME_VALID) == 0)
160 	return 0;
161 
162     tow = GET_MS_TIMEOFWEEK();
163     gps_week = GET_WEEKNUMBER();
164     session->context->leap_seconds = GET_GPS_LEAPSECONDS();
165     session->newdata.time = gpsd_gpstime_resolv(session, gps_week, tow);
166 
167     return TIME_SET | NTPTIME_IS | ONLINE_SET;
168 }
169 
170 /**
171  * GPS Satellite Info
172  */
173 static gps_mask_t
_proto__msg_svinfo(struct gps_device_t * session,unsigned char * buf,size_t data_len)174 _proto__msg_svinfo(struct gps_device_t *session, unsigned char *buf, size_t data_len)
175 {
176     unsigned char i, st, nchan, nsv;
177     unsigned int tow;
178 
179     if (data_len != SVINFO_MSG_LEN )
180 	return 0;
181 
182     GPSD_LOG(LOG_DATA, &session->context->errout,
183 	     "_proto_ SVINFO - navigation data\n");
184     /* if this protocol has a way to test message validity, use it */
185     flags = GET_FLAGS();
186     if ((flags & _PROTO__SVINFO_VALID) == 0)
187 	return 0;
188 
189     /*
190      * some protocols have a variable length message listing only visible
191      * satellites, even if there are less than the number of channels. others
192      * have a fixed length message and send empty records for idle channels
193      * that are not tracking or searching. whatever the case, nchan should
194      * be set to the number of satellites which might be visible.
195      */
196     nchan = GET_NUMBER_OF_CHANNELS();
197     if ((nchan < 1) || (nchan > MAXCHANNELS)) {
198 	GPSD_LOG(LOG_INF, &session->context->errout,
199 		 "too many channels reported\n");
200 	return 0;
201     }
202     gpsd_zero_satellites(&session->gpsdata);
203     nsv = 0; /* number of actually used satellites */
204     for (i = st = 0; i < nchan; i++) {
205 	/* get info for one channel/satellite */
206 	int off = GET_CHANNEL_STATUS(i);
207 
208 	session->gpsdata.PRN[i]		= PRN_THIS_CHANNEL_IS_TRACKING(i);
209 	session->gpsdata.ss[i]		= (float)SIGNAL_STRENGTH_FOR_CHANNEL(i);
210 	session->gpsdata.elevation[i]	= SV_ELEVATION_FOR_CHANNEL(i);
211 	session->gpsdata.azimuth[i]	= SV_AZIMUTH_FOR_CHANNEL(i);
212 
213 	if (CHANNEL_USED_IN_SOLUTION(i))
214 	    session->gpsdata.used[nsv++] = session->gpsdata.PRN[i];
215 
216 	if(session->gpsdata.PRN[i])
217 		st++;
218     }
219     /* if the satellite-info setence gives you UTC time, use it */
220     session->gpsdata.skyview_time = NaN;
221     session->gpsdata.satellites_used = nsv;
222     session->gpsdata.satellites_visible = st;
223     GPSD_LOG(LOG_DATA, &session->context->errout,
224 	     "SVINFO: visible=%d used=%d mask={SATELLITE|USED}\n",
225 	     session->gpsdata.satellites_visible,
226 	     session->gpsdata.satellites_used);
227     return SATELLITE_SET | USED_IS;
228 }
229 
230 /**
231  * Raw measurements
232  */
233 static gps_mask_t
_proto__msg_raw(struct gps_device_t * session,unsigned char * buf,size_t data_len)234 _proto__msg_raw(struct gps_device_t *session, unsigned char *buf, size_t data_len)
235 {
236     unsigned char i, st, nchan, nsv;
237     unsigned int tow;
238 
239     if (data_len != RAW_MSG_LEN )
240 	return 0;
241 
242     GPSD_LOG(LOG_DATA, &session->context->errout,
243 	     "_proto_ RAW - raw measurements\n");
244     /* if this protocol has a way to test message validity, use it */
245     flags = GET_FLAGS();
246     if ((flags & _PROTO__SVINFO_VALID) == 0)
247 	return 0;
248 
249     /*
250      * not all chipsets emit the same information. some of these observables
251      * can be easily converted into others. these are suggestions for the
252      * quantities you may wish to try extract. chipset documentation may say
253      * something like "this message contains information required to generate
254      * a RINEX file." assign NAN for unavailable data.
255      */
256     nchan = GET_NUMBER_OF_CHANNELS();
257     if ((nchan < 1) || (nchan > MAXCHANNELS)) {
258 	GPSD_LOG(LOG_INF, &session->context->errout,
259 		 "too many channels reported\n");
260 	return 0;
261     }
262 
263     DTONS(&session->raw.mtime, GET_TIME());
264 
265     /* this is so we can tell which never got set */
266     for (i = 0; i < MAXCHANNELS; i++)
267         session->gpsdata.raw.meas[i].svid = 0;
268     for (i = 0; i < n; i++){
269 	session->gpsdata.PRN[i] = GET_PRN();
270 	session->gpsdata.ss[i] = GET_SIGNAL()
271 	session->gpsdata.raw.meas[i].satstat = GET_FLAGS();
272 	session->gpsdata.raw.meas[i].pseudorange = GET_PSEUDORANGE();
273 	session->gpsdata.raw.meas[i].doppler = GET_DOPPLER();
274 	session->gpsdata.raw.meas[i].carrierphase = GET_CARRIER_PHASE();
275 	session->gpsdata.raw.meas[i].codephase = GET_CODE_PHASE();
276 	session->gpsdata.raw.meas[i].deltarange = GET_DELTA_RANGE();
277     }
278     return RAW_IS;
279 }
280 
281 /**
282  * Parse the data from the device
283  */
_proto__dispatch(struct gps_device_t * session,unsigned char * buf,size_t len)284 gps_mask_t _proto__dispatch(struct gps_device_t *session, unsigned char *buf, size_t len)
285 {
286     size_t i;
287     int type, used, visible, retmask = 0;
288 
289     if (len == 0)
290 	return 0;
291 
292     /*
293      * Set this if the driver reliably signals end of cycle.
294      * The core library zeroes it just before it calls each driver's
295      * packet analyzer.
296      */
297     session->cycle_end_reliable = true;
298     if (msgid == MY_START_OF_CYCLE)
299 	retmask |= CLEAR_IS;
300     else if (msgid == MY_END_OF_CYCLE)
301 	retmask |= REPORT_IS;
302 
303     type = GET_MESSAGE_TYPE();
304 
305     /* we may need to dump the raw packet */
306     GPSD_LOG(LOG_RAW, &session->context->errout,
307 	     "raw _proto_ packet type 0x%02x\n", type);
308 
309     switch (type)
310     {
311 	/* Deliver message to specific decoder based on message type */
312 
313     default:
314 	GPSD_LOG(LOG_WARN, &session->context->errout,
315 		 "unknown packet id %d length %d\n", type, len);
316 	return 0;
317     }
318 }
319 
320 /**********************************************************
321  *
322  * Externally called routines below here
323  *
324  **********************************************************/
325 
_proto__probe_detect(struct gps_device_t * session)326 static bool _proto__probe_detect(struct gps_device_t *session)
327 {
328    /*
329     * This method is used to elicit a positively identifying
330     * response from a candidate device. Some drivers may use
331     * this to test for the presence of a certain kernel module.
332     */
333    int test, satisfied;
334 
335    /* Your testing code here */
336    test=satisfied=0;
337    if (test==satisfied)
338       return true;
339    return false;
340 }
341 
342 #ifdef CONTROLSEND_ENABLE
343 /**
344  * Write data to the device, doing any required padding or checksumming
345  */
_proto__control_send(struct gps_device_t * session,char * msg,size_t msglen)346 static ssize_t _proto__control_send(struct gps_device_t *session,
347 			   char *msg, size_t msglen)
348 {
349    bool ok;
350 
351    /* CONSTRUCT THE MESSAGE */
352 
353    /*
354     * This copy to a public assembly buffer
355     * enables gpsmon to snoop the control message
356     * after it has been sent.
357     */
358    session->msgbuflen = msglen;
359    (void)memcpy(session->msgbuf, msg, msglen);
360 
361    /* we may need to dump the message */
362    GPSD_LOG(LOG_PROG, &session->context->errout,
363 	       "writing _proto_ control type %02x\n");
364    return gpsd_write(session, session->msgbuf, session->msgbuflen);
365 }
366 #endif /* CONTROLSEND_ENABLE */
367 
368 #ifdef RECONFIGURE_ENABLE
_proto__event_hook(struct gps_device_t * session,event_t event)369 static void _proto__event_hook(struct gps_device_t *session, event_t event)
370 {
371     if (session->context->readonly)
372 	return;
373 
374     if (event == event_wakeup) {
375        /*
376 	* Code to make the device ready to communicate.  Only needed if the
377 	* device is in some kind of sleeping state, and only shipped to
378 	* RS232C, so that gpsd won't send strings to unidentified USB devices
379 	* that might not be GPSes at all.
380 	*/
381     }
382     if (event == event_identified) {
383 	/*
384 	 * Fires when the first full packet is recognized from a
385 	 * previously unidentified device.  The session.lexer counter
386 	 * is zeroed.  If your device has a default cycle time other
387 	 * than 1 second, set session->device->gpsdata.cycle here. If
388 	 * possible, get the software version and store it in
389 	 * session->subtype.
390 	 */
391     }
392     if (event == event_configure) {
393 	/*
394 	 * Change sentence mix and set reporting modes as needed.
395 	 * Called immediately after event_identified fires, then just
396 	 * after every packet received thereafter, but you probably
397 	 * only want to take actions on the first few packets after
398 	 * the session.lexer counter has been zeroed,
399 	 *
400 	 * Remember that session->lexer.counter is available when you
401 	 * write this hook; you can use this fact to interleave configuration
402 	 * sends with the first few packet reads, which is useful for
403 	 * devices with small receive buffers.
404 	 */
405     } else if (event == event_driver_switch) {
406 	/*
407 	 * Fires when the driver on a device is changed *after* it
408 	 * has been identified.
409 	 */
410     } else if (event == event_deactivate) {
411 	/*
412 	 * Fires when the device is deactivated.  Usr this to revert
413 	 * whatever was done at event_identify and event_configure
414 	 * time.
415 	 */
416     } else if (event == event_reactivate) {
417        /*
418 	* Fires when a device is reactivated after having been closed.
419 	* Use this hook for re-establishing device settings that
420 	* it doesn't hold through closes.
421 	*/
422     }
423 }
424 
425 /*
426  * This is the entry point to the driver. When the packet sniffer recognizes
427  * a packet for this driver, it calls this method which passes the packet to
428  * the binary processor or the nmea processor, depending on the session type.
429  */
_proto__parse_input(struct gps_device_t * session)430 static gps_mask_t _proto__parse_input(struct gps_device_t *session)
431 {
432     if (session->lexer.type == _PROTO__PACKET) {
433 	return _proto__dispatch(session, session->lexer.outbuffer, session->lexer.outbuflen);
434 #ifdef NMEA0183_ENABLE
435     } else if (session->lexer.type == NMEA_PACKET) {
436 	return nmea_parse((char *)session->lexer.outbuffer, session);
437 #endif /* NMEA0183_ENABLE */
438     } else
439 	return 0;
440 }
441 
_proto__set_speed(struct gps_device_t * session,speed_t speed,char parity,int stopbits)442 static bool _proto__set_speed(struct gps_device_t *session,
443 			      speed_t speed, char parity, int stopbits)
444 {
445     /*
446      * Set port operating mode, speed, parity, stopbits etc. here.
447      * Note: parity is passed as 'N'/'E'/'O', but you should program
448      * defensively and allow 0/1/2 as well.
449      */
450 }
451 
452 /*
453  * Switch between NMEA and binary mode, if supported
454  */
_proto__set_mode(struct gps_device_t * session,int mode)455 static void _proto__set_mode(struct gps_device_t *session, int mode)
456 {
457     if (mode == MODE_NMEA) {
458 	/* send a mode switch control string */
459     } else {
460 	/* send a mode switch control string */
461     }
462 }
463 #endif /* RECONFIGURE_ENABLE */
464 
_proto_time_offset(struct gps_device_t * session)465 static double _proto_time_offset(struct gps_device_t *session)
466 {
467     /*
468      * If NTP notification is enabled, the GPS will occasionally NTP
469      * its notion of the time. This will lag behind actual time by
470      * some amount which has to be determined by observation vs. (say
471      * WWVB radio broadcasts) and, furthermore, may differ by baud
472      * rate. This method is for computing the NTP fudge factor.  If
473      * it's absent, an offset of 0.0 will be assumed, effectively
474      * falling back on what's in ntp.conf. When it returns NAN,
475      * nothing will be sent to NTP.
476      */
477     return MAGIC_CONSTANT;
478 }
479 
_proto__wrapup(struct gps_device_t * session)480 static void _proto__wrapup(struct gps_device_t *session)
481 {
482 }
483 
484 /* The methods in this code take parameters and have */
485 /* return values that conform to the requirements AT */
486 /* THE TIME THE CODE WAS WRITTEN.                    */
487 /*                                                   */
488 /* These values may well have changed by the time    */
489 /* you read this and methods could have been added   */
490 /* or deleted. Unused methods can be set to NULL.    */
491 /*                                                   */
492 /* The latest version can be found by inspecting   */
493 /* the contents of struct gps_type_t in gpsd.h.      */
494 /*                                                   */
495 /* This always contains the correct definitions that */
496 /* any driver must use to compile.                   */
497 
498 /* This is everything we export */
499 /* *INDENT-OFF* */
500 const struct gps_type_t driver__proto__binary = {
501     /* Full name of type */
502     .type_name        = "_proto",
503     /* Associated lexer packet type */
504     .packet_type      = _PROTO__PACKET,
505     /* Driver tyoe flags */
506     .flags	      = DRIVER_NOFLAGS,
507     /* Response string that identifies device (not active) */
508     .trigger          = NULL,
509     /* Number of satellite channels supported by the device */
510     .channels         = 12,
511     /* Startup-time device detector */
512     .probe_detect     = _proto__probe_detect,
513     /* Packet getter (using default routine) */
514     .get_packet       = generic_get,
515     /* Parse message packets */
516     .parse_packet     = _proto__parse_input,
517     /* RTCM handler (using default routine) */
518     .rtcm_writer      = pass_rtcm,
519     /* non-perturbing initial query (e.g. for version) */
520     .init_query        = NULL,
521     /* fire on various lifetime events */
522     .event_hook       = _proto__event_hook,
523 #ifdef RECONFIGURE_ENABLE
524     /* Speed (baudrate) switch */
525     .speed_switcher   = _proto__set_speed,
526     /* Switch to NMEA mode */
527     .mode_switcher    = _proto__set_mode,
528     /* Message delivery rate switcher (not active) */
529     .rate_switcher    = NULL,
530     /* Minimum cycle time of the device */
531     .min_cycle        = 1,
532 #endif /* RECONFIGURE_ENABLE */
533 #ifdef CONTROLSEND_ENABLE
534     /* Control string sender - should provide checksum and headers/trailer */
535     .control_send   = _proto__control_send,
536 #endif /* CONTROLSEND_ENABLE */
537     .time_offset     = _proto_time_offset,
538 /* *INDENT-ON* */
539 };
540 #endif /* defined(_PROTO__ENABLE) && defined(BINARY_ENABLE) */
541 
542