cleanup specfile for packaging
[profile/ivi/gpsd.git] / packet.c
1 /****************************************************************************
2
3 NAME:
4    packet.c -- a packet-sniffing engine for reading from GPS devices
5
6 DESCRIPTION:
7
8 Initial conditions of the problem:
9
10 1. We have a file descriptor open for (possibly non-blocking) read. The device
11    on the other end is sending packets at us.
12
13 2. It may require more than one read to gather a packet.  Reads may span packet
14    boundaries.
15
16 3. There may be leading garbage before the first packet.  After the first
17    start-of-packet, the input should be well-formed.
18
19 The problem: how do we recognize which kind of packet we're getting?
20
21 No need to handle Garmin USB binary, we know that type by the fact we're
22 connected to the Garmin kernel driver.  But we need to be able to tell the
23 others apart and distinguish them from baud barf.
24
25 PERMISSIONS
26    This file is Copyright (c) 2010 by the GPSD project
27    BSD terms apply: see the file COPYING in the distribution root for details.
28
29 ***************************************************************************/
30 #include <stdlib.h>
31 #include "gpsd_config.h"
32 #include <sys/types.h>
33 #include <ctype.h>
34 #include <stdio.h>
35 #ifndef S_SPLINT_S
36 #include <unistd.h>
37 #endif /* S_SPLINT_S */
38 #include <string.h>
39 #include <errno.h>
40 #ifndef S_SPLINT_S
41 #ifdef HAVE_NETINET_IN_H
42 #include <netinet/in.h>         /* for htons() */
43 #endif /* HAVE_NETNET_IN_H */
44 #ifdef HAVE_ARPA_INET_H
45 #include <arpa/inet.h>          /* for htons() */
46 #endif /* HAVE_ARPA_INET_H */
47 #endif /* S_SPLINT_S */
48
49 #include "bits.h"
50 #include "gpsd.h"
51 #include "crc24q.h"
52
53 /*
54  * The packet-recognition state machine.  This takes an incoming byte stream
55  * and tries to segment it into packets.  There are three types of packets:
56  *
57  * 1) Comments. These begin with # and end with \r\n.
58  *
59  * 2) NMEA lines.  These begin with $, and with \r\n, and have a checksum.
60  *
61  * 3) Binary packets.  These begin with some fixed leader character(s),
62  *    have a length embedded in them, and end with a checksum (and possibly)
63  *    some fixed trailing bytes.  
64  *
65  * 4) ISGPS packets. The input may be a bitstream containing IS-GPS-200
66  *    packets.  Each includes a fixed leader byte, a length, and check bits.  
67  *    In this case, it is not guaranted that packet starts begin on byte 
68  *    bounaries; the recognizer has to run a separate state machine against 
69  *    each byte just to achieve synchronization lock with the bitstream.
70  *
71  * Adding support for a new GPS protocol typically reqires adding state
72  * transitions to support whatever binary packet structure it has.  The
73  * goal is for the lexer to be able to cope with arbitrarily mixed packet
74  * types on the input stream.  This is a requirement because (1) sometimes
75  * gpsd wants to switch a device that supports both NMEA and a binary
76  * packet protocol to the latter for more detailed reporting, and (b) in
77  * the presence of device hotplugging, the type of GPS report coming
78  * in is subject to change at any time.
79  *
80  * Caller should consume a packet when it sees one of the *_RECOGNIZED
81  * states.  It's good practice to follow the _RECOGNIZED transition
82  * with one that recognizes a leader of the same packet type rather
83  * than dropping back to ground state -- this for example will prevent
84  * the state machine from hopping between recognizing TSIP and
85  * EverMore packets that both start with a DLE.
86  *
87  * Error handling is brutally simple; any time we see an unexpected
88  * character, go to GROUND_STATE and reset the machine (except that a
89  * $ in an NMEA payload only resets back to NMEA_DOLLAR state).  Because
90  * another good packet will usually be along in less than a second
91  * repeating the same data, Boyer-Moore-like attempts to do parallel
92  * recognition beyond the headers would make no sense in this
93  * application, they'd just add complexity.
94  *
95  * The NMEA portion of the state machine allows the following talker IDs:
96  *      GP -- Global Positioning System.
97  *      GL -- GLONASS, according to IEIC 61162-1
98  *      GN -- Mixed GPS and GLONASS data, according to IEIC 61162-1
99  *      II -- Integrated Instrumentation (Raytheon's SeaTalk system).
100  *      IN -- Integrated Navigation (Garmin uses this).
101  *
102  */
103
104 enum
105 {
106 #include "packet_states.h"
107 };
108
109 #define SOH     (unsigned char)0x01
110 #define DLE     (unsigned char)0x10
111 #define STX     (unsigned char)0x02
112 #define ETX     (unsigned char)0x03
113
114 static void character_pushback(struct gps_packet_t *lexer)
115 /* push back the last character grabbed */
116 {
117     /*@-modobserver@*//* looks like a splint bug */
118     --lexer->inbufptr;
119     /*@+modobserver@*/
120     --lexer->char_counter;
121     gpsd_report(LOG_RAW + 2, "%08ld: character pushed back\n", 
122                 lexer->char_counter);
123 }
124
125 static void nextstate(struct gps_packet_t *lexer, unsigned char c)
126 {
127 #ifdef RTCM104V2_ENABLE
128     enum isgpsstat_t isgpsstat;
129 #endif /* RTCM104V2_ENABLE */
130 #ifdef SUPERSTAR2_ENABLE
131     static unsigned char ctmp;
132 #endif /* SUPERSTAR2_ENABLE */
133 /*@ +charint -casebreak @*/
134     switch (lexer->state) {
135     case GROUND_STATE:
136         if (c == '#') {
137             lexer->state = COMMENT_BODY;
138             break;
139         }
140 #ifdef NMEA_ENABLE
141         if (c == '$') {
142             lexer->state = NMEA_DOLLAR;
143             break;
144         }
145         if (c == '!') {
146             lexer->state = NMEA_BANG;
147             break;
148         }
149 #endif /* NMEA_ENABLE */
150 #if defined(TNT_ENABLE) || defined(GARMINTXT_ENABLE) || defined(ONCORE_ENABLE)
151         if (c == '@') {
152             lexer->state = AT1_LEADER;
153             break;
154         }
155 #endif
156 #ifdef SIRF_ENABLE
157         if (c == 0xa0) {
158             lexer->state = SIRF_LEADER_1;
159             break;
160         }
161 #endif /* SIRF_ENABLE */
162 #ifdef SUPERSTAR2_ENABLE
163         if (c == SOH) {
164             lexer->state = SUPERSTAR2_LEADER;
165             break;
166         }
167 #endif /* SUPERSTAR2_ENABLE */
168 #if defined(TSIP_ENABLE) || defined(EVERMORE_ENABLE) || defined(GARMIN_ENABLE)
169         if (c == DLE) {
170             lexer->state = DLE_LEADER;
171             break;
172         }
173 #endif /* TSIP_ENABLE || EVERMORE_ENABLE || GARMIN_ENABLE */
174 #ifdef TRIPMATE_ENABLE
175         if (c == 'A') {
176 #ifdef RTCM104V2_ENABLE
177             if (rtcm2_decode(lexer, c) == ISGPS_MESSAGE) {
178                 lexer->state = RTCM2_RECOGNIZED;
179                 break;
180             }
181 #endif /* RTCM104V2_ENABLE */
182             lexer->state = ASTRAL_1;
183             break;
184         }
185 #endif /* TRIPMATE_ENABLE */
186 #ifdef EARTHMATE_ENABLE
187         if (c == 'E') {
188 #ifdef RTCM104V2_ENABLE
189             if (rtcm2_decode(lexer, c) == ISGPS_MESSAGE) {
190                 lexer->state = RTCM2_RECOGNIZED;
191                 break;
192             }
193 #endif /* RTCM104V2_ENABLE */
194             lexer->state = EARTHA_1;
195             break;
196         }
197 #endif /* EARTHMATE_ENABLE */
198 #ifdef ZODIAC_ENABLE
199         if (c == 0xff) {
200             lexer->state = ZODIAC_LEADER_1;
201             break;
202         }
203 #endif /* ZODIAC_ENABLE */
204 #ifdef UBX_ENABLE
205         if (c == 0xb5) {
206             lexer->state = UBX_LEADER_1;
207             break;
208         }
209 #endif /* UBX_ENABLE */
210 #ifdef ITRAX_ENABLE
211         if (c == '<') {
212             lexer->state = ITALK_LEADER_1;
213             break;
214         }
215 #endif /* ITRAX_ENABLE */
216 #ifdef NAVCOM_ENABLE
217         if (c == 0x02) {
218             lexer->state = NAVCOM_LEADER_1;
219             break;
220         }
221 #endif /* NAVCOM_ENABLE */
222 #ifdef RTCM104V2_ENABLE
223         if ((isgpsstat = rtcm2_decode(lexer, c)) == ISGPS_SYNC) {
224             lexer->state = RTCM2_SYNC_STATE;
225             break;
226         } else if (isgpsstat == ISGPS_MESSAGE) {
227             lexer->state = RTCM2_RECOGNIZED;
228             break;
229         }
230 #endif /* RTCM104V2_ENABLE */
231 #ifdef RTCM104V3_ENABLE
232         if (c == 0xD3) {
233             lexer->state = RTCM3_LEADER_1;
234             break;
235         }
236 #endif /* RTCM104V3_ENABLE */
237         break;
238     case COMMENT_BODY:
239         if (c == '\n')
240             lexer->state = COMMENT_RECOGNIZED;
241         else if (!isprint(c))
242             lexer->state = GROUND_STATE;
243         break;
244 #ifdef NMEA_ENABLE
245     case NMEA_DOLLAR:
246         if (c == 'G')
247             lexer->state = NMEA_PUB_LEAD;
248         else if (c == 'P')      /* vendor sentence */
249             lexer->state = NMEA_VENDOR_LEAD;
250         else if (c == 'I')      /* Seatalk */
251             lexer->state = SEATALK_LEAD_1;
252         else if (c == 'A')      /* SiRF Ack */
253             lexer->state = SIRF_ACK_LEAD_1;
254 #ifdef OCEANSERVER_ENABLE
255         else if (c == 'C')
256             lexer->state = NMEA_LEADER_END;
257 #endif /* OCEANSERVER_ENABLE */
258         else
259             lexer->state = GROUND_STATE;
260         break;
261     case NMEA_PUB_LEAD:
262         /*
263          * $GP == GPS, $GL = GLONASS only, $GN = mixed GPS and GLONASS,
264          * according to NMEA (IEIC 61162-1) DRAFT 02/06/2009.
265          */
266         if (c == 'P' || c == 'N' || c == 'L')
267             lexer->state = NMEA_LEADER_END;
268         else
269             lexer->state = GROUND_STATE;
270         break;
271     case NMEA_VENDOR_LEAD:
272         if (c == 'A')
273             lexer->state = NMEA_PASHR_A;
274         else if (isalpha(c))
275             lexer->state = NMEA_LEADER_END;
276         else
277             lexer->state = GROUND_STATE;
278         break;
279     /*
280      * Without the following six states, DLE in a $PASHR can fool the
281      * sniffer into thinking it sees a TSIP packet.  Hilarity ensues.
282      */
283     case NMEA_PASHR_A:
284         if (c == 'S')
285             lexer->state = NMEA_PASHR_S;
286         else if (isalpha(c))
287             lexer->state = NMEA_LEADER_END;
288         else
289             lexer->state = GROUND_STATE;
290         break;
291     case NMEA_PASHR_S:
292         if (c == 'H')
293             lexer->state = NMEA_PASHR_H;
294         else if (isalpha(c))
295             lexer->state = NMEA_LEADER_END;
296         else
297             lexer->state = GROUND_STATE;
298         break;
299     case NMEA_PASHR_H:
300         if (c == 'R')
301             lexer->state = NMEA_BINARY_BODY;
302         else if (isalpha(c))
303             lexer->state = NMEA_LEADER_END;
304         else
305             lexer->state = GROUND_STATE;
306         break;
307     case NMEA_BINARY_BODY:
308         if (c == '\r')
309             lexer->state = NMEA_BINARY_CR;
310         break;
311     case NMEA_BINARY_CR:
312         if (c == '\n')
313             lexer->state = NMEA_BINARY_NL;
314         else
315             lexer->state = NMEA_BINARY_BODY;
316         break;
317     case NMEA_BINARY_NL:
318         if (c == '$') {
319             character_pushback(lexer);
320             lexer->state = NMEA_RECOGNIZED;     /* CRC will reject it */
321         } else
322             lexer->state = NMEA_BINARY_BODY;
323         break;
324     case NMEA_BANG:
325         if (c == 'A')
326             lexer->state = AIS_LEAD_1;
327         else
328             lexer->state = GROUND_STATE;
329         break;
330     case AIS_LEAD_1:
331         if (c == 'I')
332             lexer->state = AIS_LEAD_2;
333         else
334             lexer->state = GROUND_STATE;
335         break;
336     case AIS_LEAD_2:
337         if (isalpha(c))
338             lexer->state = NMEA_LEADER_END;
339         else
340             lexer->state = GROUND_STATE;
341         break;
342 #if defined(TNT_ENABLE) || defined(GARMINTXT_ENABLE) || defined(ONCORE_ENABLE)
343     case AT1_LEADER:
344         switch (c) {
345 #ifdef ONCORE_ENABLE
346         case '@':
347             lexer->state = ONCORE_AT2;
348             break;
349 #endif /* ONCORE_ENABLE */
350 #ifdef TNT_ENABLE
351         case '*':
352             /*
353              * TNT has similar structure to NMEA packet, '*' before
354              * optional checksum ends the packet. Since '*' cannot be
355              * received from GARMIN working in TEXT mode, use this
356              * difference to tell that this is not GARMIN TEXT packet,
357              * could be TNT.
358              */
359             lexer->state = NMEA_LEADER_END;
360             break;
361 #endif /* TNT_ENABLE */
362 #if defined(GARMINTXT_ENABLE)
363         case '\r':
364             /* stay in this state, next character should be '\n' */
365             /* in the theory we can stop search here and don't wait for '\n' */
366             lexer->state = AT1_LEADER;
367             break;
368         case '\n':
369             /* end of packet found */
370             lexer->state = GTXT_RECOGNIZED;
371             break;
372 #endif /* GARMINTXT_ENABLE */
373         default:
374             if (!isprint(c))
375                 lexer->state = GROUND_STATE;
376         }
377         break;
378 #endif /* defined(TNT_ENABLE) || defined(GARMINTXT_ENABLE) || defined(ONCORE_ENABLE) */
379     case NMEA_LEADER_END:
380         if (c == '\r')
381             lexer->state = NMEA_CR;
382         else if (c == '\n')
383             /* not strictly correct, but helps for interpreting logfiles */
384             lexer->state = NMEA_RECOGNIZED;
385         else if (c == '$')
386             /* faster recovery from missing sentence trailers */
387             lexer->state = NMEA_DOLLAR;
388         else if (!isprint(c))
389             lexer->state = GROUND_STATE;
390         break;
391     case NMEA_CR:
392         if (c == '\n')
393             lexer->state = NMEA_RECOGNIZED;
394         /*
395          * There's a GPS called a Jackson Labs Firefly-1a that emits \r\r\n
396          * at the end of each sentence.  Don't be confused by this.
397          */
398         else if (c == '\r')
399             lexer->state = NMEA_CR;
400         else
401             lexer->state = GROUND_STATE;
402         break;
403     case NMEA_RECOGNIZED:
404         if (c == '#')
405             lexer->state = COMMENT_BODY;
406         else if (c == '$')
407             lexer->state = NMEA_DOLLAR;
408         else if (c == '!')
409             lexer->state = NMEA_BANG;
410 #ifdef UBX_ENABLE
411         else if (c == 0xb5)     /* LEA-5H can and will output NMEA and UBX back to back */
412             lexer->state = UBX_LEADER_1;
413 #endif
414         else
415             lexer->state = GROUND_STATE;
416         break;
417     case SEATALK_LEAD_1:
418         if (c == 'I' || c == 'N')       /* II or IN are accepted */
419             lexer->state = NMEA_LEADER_END;
420         else
421             lexer->state = GROUND_STATE;
422         break;
423 #ifdef TRIPMATE_ENABLE
424     case ASTRAL_1:
425         if (c == 'S') {
426 #ifdef RTCM104V2_ENABLE
427             if (rtcm2_decode(lexer, c) == ISGPS_MESSAGE) {
428                 lexer->state = RTCM2_RECOGNIZED;
429                 break;
430             }
431 #endif /* RTCM104V2_ENABLE */
432             lexer->state = ASTRAL_2;
433         } else
434             lexer->state = GROUND_STATE;
435         break;
436     case ASTRAL_2:
437         if (c == 'T') {
438 #ifdef RTCM104V2_ENABLE
439             if (rtcm2_decode(lexer, c) == ISGPS_MESSAGE) {
440                 lexer->state = RTCM2_RECOGNIZED;
441                 break;
442             }
443 #endif /* RTCM104V2_ENABLE */
444             lexer->state = ASTRAL_3;
445         } else
446             lexer->state = GROUND_STATE;
447         break;
448     case ASTRAL_3:
449         if (c == 'R') {
450 #ifdef RTCM104V2_ENABLE
451             if (rtcm2_decode(lexer, c) == ISGPS_MESSAGE) {
452                 lexer->state = RTCM2_RECOGNIZED;
453                 break;
454             }
455 #endif /* RTCM104V2_ENABLE */
456             lexer->state = ASTRAL_5;
457         } else
458             lexer->state = GROUND_STATE;
459         break;
460     case ASTRAL_4:
461         if (c == 'A') {
462 #ifdef RTCM104V2_ENABLE
463             if (rtcm2_decode(lexer, c) == ISGPS_MESSAGE) {
464                 lexer->state = RTCM2_RECOGNIZED;
465                 break;
466             }
467 #endif /* RTCM104V2_ENABLE */
468             lexer->state = ASTRAL_2;
469         } else
470             lexer->state = GROUND_STATE;
471         break;
472     case ASTRAL_5:
473         if (c == 'L') {
474 #ifdef RTCM104V2_ENABLE
475             if (rtcm2_decode(lexer, c) == ISGPS_MESSAGE) {
476                 lexer->state = RTCM2_RECOGNIZED;
477                 break;
478             }
479 #endif /* RTCM104V2_ENABLE */
480             lexer->state = NMEA_RECOGNIZED;
481         } else
482             lexer->state = GROUND_STATE;
483         break;
484 #endif /* TRIPMATE_ENABLE */
485 #ifdef EARTHMATE_ENABLE
486     case EARTHA_1:
487         if (c == 'A') {
488 #ifdef RTCM104V2_ENABLE
489             if (rtcm2_decode(lexer, c) == ISGPS_MESSAGE) {
490                 lexer->state = RTCM2_RECOGNIZED;
491                 break;
492             }
493 #endif /* RTCM104V2_ENABLE */
494             lexer->state = EARTHA_2;
495         } else
496             lexer->state = GROUND_STATE;
497         break;
498     case EARTHA_2:
499         if (c == 'R') {
500 #ifdef RTCM104V2_ENABLE
501             if (rtcm2_decode(lexer, c) == ISGPS_MESSAGE) {
502                 lexer->state = RTCM2_RECOGNIZED;
503                 break;
504             }
505 #endif /* RTCM104V2_ENABLE */
506             lexer->state = EARTHA_3;
507         } else
508             lexer->state = GROUND_STATE;
509         break;
510     case EARTHA_3:
511         if (c == 'T') {
512 #ifdef RTCM104V2_ENABLE
513             if (rtcm2_decode(lexer, c) == ISGPS_MESSAGE) {
514                 lexer->state = RTCM2_RECOGNIZED;
515                 break;
516             }
517 #endif /* RTCM104V2_ENABLE */
518             lexer->state = EARTHA_4;
519         } else
520             lexer->state = GROUND_STATE;
521         break;
522     case EARTHA_4:
523         if (c == 'H') {
524 #ifdef RTCM104V2_ENABLE
525             if (rtcm2_decode(lexer, c) == ISGPS_MESSAGE) {
526                 lexer->state = RTCM2_RECOGNIZED;
527                 break;
528             }
529 #endif /* RTCM104V2_ENABLE */
530             lexer->state = EARTHA_5;
531         } else
532             lexer->state = GROUND_STATE;
533         break;
534     case EARTHA_5:
535         if (c == 'A') {
536 #ifdef RTCM104V2_ENABLE
537             if (rtcm2_decode(lexer, c) == ISGPS_MESSAGE) {
538                 lexer->state = RTCM2_RECOGNIZED;
539                 break;
540             }
541 #endif /* RTCM104V2_ENABLE */
542             lexer->state = NMEA_RECOGNIZED;
543         } else
544             lexer->state = GROUND_STATE;
545         break;
546 #endif /* EARTHMATE_ENABLE */
547     case SIRF_ACK_LEAD_1:
548         if (c == 'c')
549             lexer->state = SIRF_ACK_LEAD_2;
550         else if (c == 'I')
551             lexer->state = AIS_LEAD_2;
552         else
553             lexer->state = GROUND_STATE;
554         break;
555     case SIRF_ACK_LEAD_2:
556         if (c == 'k')
557             lexer->state = NMEA_LEADER_END;
558         else
559             lexer->state = GROUND_STATE;
560         break;
561 #endif /* NMEA_ENABLE */
562 #ifdef SIRF_ENABLE
563     case SIRF_LEADER_1:
564         if (c == 0xa2)
565             lexer->state = SIRF_LEADER_2;
566         else
567             lexer->state = GROUND_STATE;
568         break;
569     case SIRF_LEADER_2:
570         lexer->length = (size_t) (c << 8);
571         lexer->state = SIRF_LENGTH_1;
572         break;
573     case SIRF_LENGTH_1:
574         lexer->length += c + 2;
575         if (lexer->length <= MAX_PACKET_LENGTH)
576             lexer->state = SIRF_PAYLOAD;
577         else
578             lexer->state = GROUND_STATE;
579         break;
580     case SIRF_PAYLOAD:
581         if (--lexer->length == 0)
582             lexer->state = SIRF_DELIVERED;
583         break;
584     case SIRF_DELIVERED:
585         if (c == 0xb0)
586             lexer->state = SIRF_TRAILER_1;
587         else
588             lexer->state = GROUND_STATE;
589         break;
590     case SIRF_TRAILER_1:
591         if (c == 0xb3)
592             lexer->state = SIRF_RECOGNIZED;
593         else
594             lexer->state = GROUND_STATE;
595         break;
596     case SIRF_RECOGNIZED:
597         if (c == 0xa0)
598             lexer->state = SIRF_LEADER_1;
599         else
600             lexer->state = GROUND_STATE;
601         break;
602 #endif /* SIRF_ENABLE */
603 #ifdef SUPERSTAR2_ENABLE
604     case SUPERSTAR2_LEADER:
605         ctmp = c;
606         lexer->state = SUPERSTAR2_ID1;
607         break;
608     case SUPERSTAR2_ID1:
609         if ((ctmp ^ 0xff) == c)
610             lexer->state = SUPERSTAR2_ID2;
611         else
612             lexer->state = GROUND_STATE;
613         break;
614     case SUPERSTAR2_ID2:
615         lexer->length = (size_t) c;     /* how many data bytes follow this byte */
616         if (lexer->length)
617             lexer->state = SUPERSTAR2_PAYLOAD;
618         else
619             lexer->state = SUPERSTAR2_CKSUM1;   /* no data, jump to checksum */
620         break;
621     case SUPERSTAR2_PAYLOAD:
622         if (--lexer->length == 0)
623             lexer->state = SUPERSTAR2_CKSUM1;
624         break;
625     case SUPERSTAR2_CKSUM1:
626         lexer->state = SUPERSTAR2_CKSUM2;
627         break;
628     case SUPERSTAR2_CKSUM2:
629         lexer->state = SUPERSTAR2_RECOGNIZED;
630         break;
631     case SUPERSTAR2_RECOGNIZED:
632         if (c == SOH)
633             lexer->state = SUPERSTAR2_LEADER;
634         else
635             lexer->state = GROUND_STATE;
636         break;
637 #endif /* SUPERSTAR2_ENABLE */
638 #ifdef ONCORE_ENABLE
639     case ONCORE_AT2:
640         if (isupper(c)) {
641             lexer->length = (size_t) c;
642             lexer->state = ONCORE_ID1;
643         } else
644             lexer->state = GROUND_STATE;
645         break;
646     case ONCORE_ID1:
647         if (isalpha(c)) {
648             lexer->length =
649                 oncore_payload_cksum_length((unsigned char)lexer->length, c);
650             if (lexer->length != 0) {
651                 lexer->state = ONCORE_PAYLOAD;
652                 break;
653             }
654         }
655         lexer->state = GROUND_STATE;
656         break;
657     case ONCORE_PAYLOAD:
658         if (--lexer->length == 0)
659             lexer->state = ONCORE_CHECKSUM;
660         break;
661     case ONCORE_CHECKSUM:
662         if (c != '\r')
663             lexer->state = GROUND_STATE;
664         else
665             lexer->state = ONCORE_CR;
666         break;
667     case ONCORE_CR:
668         if (c == '\n')
669             lexer->state = ONCORE_RECOGNIZED;
670         else
671             lexer->state = ONCORE_PAYLOAD;
672         break;
673     case ONCORE_RECOGNIZED:
674         if (c == '@')
675             lexer->state = AT1_LEADER;
676         else
677             lexer->state = GROUND_STATE;
678         break;
679 #endif /* ONCORE_ENABLE */
680 #if defined(TSIP_ENABLE) || defined(EVERMORE_ENABLE) || defined(GARMIN_ENABLE)
681     case DLE_LEADER:
682 #ifdef EVERMORE_ENABLE
683         if (c == STX) {
684             lexer->state = EVERMORE_LEADER_2;
685             break;
686         }
687 #endif /* EVERMORE_ENABLE */
688 #if defined(TSIP_ENABLE) || defined(GARMIN_ENABLE) || defined(NAVCOM_ENABLE)
689         /* garmin is special case of TSIP */
690         /* check last because there's no checksum */
691 #if defined(TSIP_ENABLE)
692         if (c >= 0x13) {
693             lexer->state = TSIP_PAYLOAD;
694             break;
695         }
696 #endif /* TSIP_ENABLE */
697         if (c == DLE) {
698             lexer->state = GROUND_STATE;
699             break;
700         }
701         // FALL-THRU!!!!! no break here
702 #endif /* TSIP_ENABLE */
703 #ifdef NAVCOM_ENABLE
704     case NAVCOM_LEADER_1:
705         if (c == 0x99)
706             lexer->state = NAVCOM_LEADER_2;
707         else
708             lexer->state = GROUND_STATE;
709         break;
710     case NAVCOM_LEADER_2:
711         if (c == 0x66)
712             lexer->state = NAVCOM_LEADER_3;
713         else
714             lexer->state = GROUND_STATE;
715         break;
716     case NAVCOM_LEADER_3:
717         lexer->state = NAVCOM_ID;
718         break;
719     case NAVCOM_ID:
720         lexer->length = (size_t) c - 4;
721         lexer->state = NAVCOM_LENGTH_1;
722         break;
723     case NAVCOM_LENGTH_1:
724         lexer->length += (c << 8);
725         lexer->state = NAVCOM_LENGTH_2;
726         break;
727     case NAVCOM_LENGTH_2:
728         if (--lexer->length == 0)
729             lexer->state = NAVCOM_PAYLOAD;
730         break;
731     case NAVCOM_PAYLOAD:
732     {
733         unsigned int n;
734         unsigned char csum = lexer->inbuffer[3];
735         for (n = 4;
736              (unsigned char *)(lexer->inbuffer + n) < lexer->inbufptr - 1;
737              n++)
738             csum ^= lexer->inbuffer[n];
739         if (csum != c) {
740             gpsd_report(LOG_IO,
741                         "Navcom packet type 0x%hx bad checksum 0x%hx, expecting 0x%hx\n",
742                         lexer->inbuffer[3], csum, c);
743             gpsd_report(LOG_RAW, "Navcom packet dump: %s\n",
744                         gpsd_hexdump_wrapper(lexer->inbuffer, lexer->inbuflen,
745                                              LOG_RAW));
746             lexer->state = GROUND_STATE;
747             break;
748         }
749     }
750         lexer->state = NAVCOM_CSUM;
751         break;
752     case NAVCOM_CSUM:
753         if (c == 0x03)
754             lexer->state = NAVCOM_RECOGNIZED;
755         else
756             lexer->state = GROUND_STATE;
757         break;
758     case NAVCOM_RECOGNIZED:
759         if (c == 0x02)
760             lexer->state = NAVCOM_LEADER_1;
761         else
762             lexer->state = GROUND_STATE;
763         break;
764 #endif /* NAVCOM_ENABLE */
765 #endif /* TSIP_ENABLE || EVERMORE_ENABLE || GARMIN_ENABLE */
766 #ifdef RTCM104V3_ENABLE
767     case RTCM3_LEADER_1:
768         if ((c & 0xFC) == 0) {
769             lexer->length = (size_t) (c << 8);
770             lexer->state = RTCM3_LEADER_2;
771             break;
772         } else
773             lexer->state = GROUND_STATE;
774         break;
775     case RTCM3_LEADER_2:
776         lexer->length |= c;
777         lexer->length += 3;     /* to get the three checksum bytes */
778         lexer->state = RTCM3_PAYLOAD;
779         break;
780     case RTCM3_PAYLOAD:
781         if (--lexer->length == 0)
782             lexer->state = RTCM3_RECOGNIZED;
783         break;
784 #endif /* RTCM104V3_ENABLE */
785 #ifdef ZODIAC_ENABLE
786     case ZODIAC_EXPECTED:
787     case ZODIAC_RECOGNIZED:
788         if (c == 0xff)
789             lexer->state = ZODIAC_LEADER_1;
790         else
791             lexer->state = GROUND_STATE;
792         break;
793     case ZODIAC_LEADER_1:
794         if (c == 0x81)
795             lexer->state = ZODIAC_LEADER_2;
796         else
797             lexer->state = GROUND_STATE;
798         break;
799     case ZODIAC_LEADER_2:
800         lexer->state = ZODIAC_ID_1;
801         break;
802     case ZODIAC_ID_1:
803         lexer->state = ZODIAC_ID_2;
804         break;
805     case ZODIAC_ID_2:
806         lexer->length = (size_t) c;
807         lexer->state = ZODIAC_LENGTH_1;
808         break;
809     case ZODIAC_LENGTH_1:
810         lexer->length += (c << 8);
811         lexer->state = ZODIAC_LENGTH_2;
812         break;
813     case ZODIAC_LENGTH_2:
814         lexer->state = ZODIAC_FLAGS_1;
815         break;
816     case ZODIAC_FLAGS_1:
817         lexer->state = ZODIAC_FLAGS_2;
818         break;
819     case ZODIAC_FLAGS_2:
820         lexer->state = ZODIAC_HSUM_1;
821         break;
822     case ZODIAC_HSUM_1:
823     {
824 #define getword(i) (short)(lexer->inbuffer[2*(i)] | (lexer->inbuffer[2*(i)+1] << 8))
825         short sum = getword(0) + getword(1) + getword(2) + getword(3);
826         sum *= -1;
827         if (sum != getword(4)) {
828             gpsd_report(LOG_IO,
829                         "Zodiac Header checksum 0x%hx expecting 0x%hx\n",
830                         sum, getword(4));
831             lexer->state = GROUND_STATE;
832             break;
833         }
834     }
835         gpsd_report(LOG_RAW + 1, "Zodiac header id=%hd len=%hd flags=%hx\n",
836                     getword(1), getword(2), getword(3));
837 #undef getword
838         if (lexer->length == 0) {
839             lexer->state = ZODIAC_RECOGNIZED;
840             break;
841         }
842         lexer->length *= 2;     /* word count to byte count */
843         lexer->length += 2;     /* checksum */
844         /* 10 bytes is the length of the Zodiac header */
845         if (lexer->length <= MAX_PACKET_LENGTH - 10)
846             lexer->state = ZODIAC_PAYLOAD;
847         else
848             lexer->state = GROUND_STATE;
849         break;
850     case ZODIAC_PAYLOAD:
851         if (--lexer->length == 0)
852             lexer->state = ZODIAC_RECOGNIZED;
853         break;
854 #endif /* ZODIAC_ENABLE */
855 #ifdef UBX_ENABLE
856     case UBX_LEADER_1:
857         if (c == 0x62)
858             lexer->state = UBX_LEADER_2;
859         else
860             lexer->state = GROUND_STATE;
861         break;
862     case UBX_LEADER_2:
863         lexer->state = UBX_CLASS_ID;
864         break;
865     case UBX_CLASS_ID:
866         lexer->state = UBX_MESSAGE_ID;
867         break;
868     case UBX_MESSAGE_ID:
869         lexer->length = (size_t) c;
870         lexer->state = UBX_LENGTH_1;
871         break;
872     case UBX_LENGTH_1:
873         lexer->length += (c << 8);
874         if (lexer->length <= MAX_PACKET_LENGTH)
875             lexer->state = UBX_LENGTH_2;
876         else
877             lexer->state = GROUND_STATE;
878         break;
879     case UBX_LENGTH_2:
880         lexer->state = UBX_PAYLOAD;
881         break;
882     case UBX_PAYLOAD:
883         if (--lexer->length == 0)
884             lexer->state = UBX_CHECKSUM_A;
885         /* else stay in payload state */
886         break;
887     case UBX_CHECKSUM_A:
888         lexer->state = UBX_RECOGNIZED;
889         break;
890     case UBX_RECOGNIZED:
891         if (c == 0xb5)
892             lexer->state = UBX_LEADER_1;
893 #ifdef NMEA_ENABLE
894         else if (c == '$')      /* LEA-5H can and will output NMEA and UBX back to back */
895             lexer->state = NMEA_DOLLAR;
896 #endif /* NMEA_ENABLE */
897         else
898             lexer->state = GROUND_STATE;
899         break;
900 #endif /* UBX_ENABLE */
901 #ifdef EVERMORE_ENABLE
902     case EVERMORE_LEADER_1:
903         if (c == STX)
904             lexer->state = EVERMORE_LEADER_2;
905         else
906             lexer->state = GROUND_STATE;
907         break;
908     case EVERMORE_LEADER_2:
909         lexer->length = (size_t) c;
910         if (c == DLE)
911             lexer->state = EVERMORE_PAYLOAD_DLE;
912         else
913             lexer->state = EVERMORE_PAYLOAD;
914         break;
915     case EVERMORE_PAYLOAD:
916         if (c == DLE)
917             lexer->state = EVERMORE_PAYLOAD_DLE;
918         else if (--lexer->length == 0)
919             lexer->state = GROUND_STATE;
920         break;
921     case EVERMORE_PAYLOAD_DLE:
922         switch (c) {
923         case DLE:
924             lexer->state = EVERMORE_PAYLOAD;
925             break;
926         case ETX:
927             lexer->state = EVERMORE_RECOGNIZED;
928             break;
929         default:
930             lexer->state = GROUND_STATE;
931         }
932         break;
933     case EVERMORE_RECOGNIZED:
934         if (c == DLE)
935             lexer->state = EVERMORE_LEADER_1;
936         else
937             lexer->state = GROUND_STATE;
938         break;
939 #endif /* EVERMORE_ENABLE */
940 #ifdef ITRAX_ENABLE
941     case ITALK_LEADER_1:
942         if (c == '!')
943             lexer->state = ITALK_LEADER_2;
944         else
945             lexer->state = GROUND_STATE;
946         break;
947     case ITALK_LEADER_2:
948         lexer->length = (size_t) (lexer->inbuffer[6] & 0xff);
949         lexer->state = ITALK_LENGTH;
950         break;
951     case ITALK_LENGTH:
952         lexer->length += 1;     /* fix number of words in payload */
953         lexer->length *= 2;     /* convert to number of bytes */
954         lexer->length += 3;     /* add trailer length */
955         lexer->state = ITALK_PAYLOAD;
956         break;
957     case ITALK_PAYLOAD:
958         /* lookahead for "<!" because sometimes packets are short but valid */
959         if ((c == '>') && (lexer->inbufptr[0] == '<') &&
960             (lexer->inbufptr[1] == '!')) {
961             lexer->state = ITALK_RECOGNIZED;
962             gpsd_report(LOG_IO, "ITALK: trying to process runt packet\n");
963             break;
964         } else if (--lexer->length == 0)
965             lexer->state = ITALK_DELIVERED;
966         break;
967     case ITALK_DELIVERED:
968         if (c == '>')
969             lexer->state = ITALK_RECOGNIZED;
970         else
971             lexer->state = GROUND_STATE;
972         break;
973     case ITALK_RECOGNIZED:
974         if (c == '<')
975             lexer->state = ITALK_LEADER_1;
976         else
977             lexer->state = GROUND_STATE;
978         break;
979 #endif /* ITRAX_ENABLE */
980 #ifdef TSIP_ENABLE
981     case TSIP_LEADER:
982         /* unused case */
983         if (c >= 0x13)
984             lexer->state = TSIP_PAYLOAD;
985         else
986             lexer->state = GROUND_STATE;
987         break;
988     case TSIP_PAYLOAD:
989         if (c == DLE)
990             lexer->state = TSIP_DLE;
991         break;
992     case TSIP_DLE:
993         switch (c) {
994         case ETX:
995             lexer->state = TSIP_RECOGNIZED;
996             break;
997         case DLE:
998             lexer->state = TSIP_PAYLOAD;
999             break;
1000         default:
1001             lexer->state = GROUND_STATE;
1002             break;
1003         }
1004         break;
1005     case TSIP_RECOGNIZED:
1006         if (c == DLE)
1007             /*
1008              * Don't go to TSIP_LEADER state -- TSIP packets aren't
1009              * checksummed, so false positives are easy.  We might be
1010              * looking at another DLE-stuffed protocol like EverMore
1011              * or Garmin streaming binary.
1012              */
1013             lexer->state = DLE_LEADER;
1014         else
1015             lexer->state = GROUND_STATE;
1016         break;
1017 #endif /* TSIP_ENABLE */
1018 #ifdef RTCM104V2_ENABLE
1019     case RTCM2_SYNC_STATE:
1020     case RTCM2_SKIP_STATE:
1021         if ((isgpsstat = rtcm2_decode(lexer, c)) == ISGPS_MESSAGE) {
1022             lexer->state = RTCM2_RECOGNIZED;
1023             break;
1024         } else if (isgpsstat == ISGPS_NO_SYNC)
1025             lexer->state = GROUND_STATE;
1026         break;
1027
1028     case RTCM2_RECOGNIZED:
1029         if (rtcm2_decode(lexer, c) == ISGPS_SYNC) {
1030             lexer->state = RTCM2_SYNC_STATE;
1031             break;
1032         } else
1033             lexer->state = GROUND_STATE;
1034         break;
1035 #endif /* RTCM104V2_ENABLE */
1036     }
1037 /*@ -charint +casebreak @*/
1038 }
1039
1040 #define STATE_DEBUG
1041
1042 static void packet_accept(struct gps_packet_t *lexer, int packet_type)
1043 /* packet grab succeeded, move to output buffer */
1044 {
1045     size_t packetlen = lexer->inbufptr - lexer->inbuffer;
1046     if (packetlen < sizeof(lexer->outbuffer)) {
1047         memcpy(lexer->outbuffer, lexer->inbuffer, packetlen);
1048         lexer->outbuflen = packetlen;
1049         lexer->outbuffer[packetlen] = '\0';
1050         lexer->type = packet_type;
1051 #ifdef STATE_DEBUG
1052         gpsd_report(LOG_RAW + 1, "Packet type %d accepted %zu = %s\n",
1053                     packet_type, packetlen,
1054                     gpsd_hexdump_wrapper(lexer->outbuffer, lexer->outbuflen,
1055                                          LOG_IO));
1056 #endif /* STATE_DEBUG */
1057     } else {
1058         gpsd_report(LOG_ERROR, "Rejected too long packet type %d len %zu\n",
1059                     packet_type, packetlen);
1060     }
1061 }
1062
1063 static void packet_discard(struct gps_packet_t *lexer)
1064 /* shift the input buffer to discard all data up to current input pointer */
1065 {
1066     size_t discard = lexer->inbufptr - lexer->inbuffer;
1067     size_t remaining = lexer->inbuflen - discard;
1068     lexer->inbufptr = memmove(lexer->inbuffer, lexer->inbufptr, remaining);
1069     lexer->inbuflen = remaining;
1070 #ifdef STATE_DEBUG
1071     gpsd_report(LOG_RAW + 1,
1072                 "Packet discard of %zu, chars remaining is %zu = %s\n",
1073                 discard, remaining,
1074                 gpsd_hexdump_wrapper(lexer->inbuffer, lexer->inbuflen,
1075                                      LOG_RAW));
1076 #endif /* STATE_DEBUG */
1077 }
1078
1079 static void character_discard(struct gps_packet_t *lexer)
1080 /* shift the input buffer to discard one character and reread data */
1081 {
1082     memmove(lexer->inbuffer, lexer->inbuffer + 1, (size_t)-- lexer->inbuflen);
1083     lexer->inbufptr = lexer->inbuffer;
1084 #ifdef STATE_DEBUG
1085     gpsd_report(LOG_RAW + 1, "Character discarded, buffer %zu chars = %s\n",
1086                 lexer->inbuflen,
1087                 gpsd_hexdump_wrapper(lexer->inbuffer, lexer->inbuflen,
1088                                      LOG_RAW));
1089 #endif /* STATE_DEBUG */
1090 }
1091
1092 /* get 0-origin big-endian words relative to start of packet buffer */
1093 #define getword(i) (short)(lexer->inbuffer[2*(i)] | (lexer->inbuffer[2*(i)+1] << 8))
1094
1095 /* entry points begin here */
1096
1097 void packet_init( /*@out@*/ struct gps_packet_t *lexer)
1098 {
1099     lexer->char_counter = 0;
1100     lexer->retry_counter = 0;
1101     packet_reset(lexer);
1102 }
1103
1104 void packet_parse(struct gps_packet_t *lexer)
1105 /* grab a packet from the input buffer */
1106 {
1107     lexer->outbuflen = 0;
1108     while (packet_buffered_input(lexer) > 0) {
1109         /*@ -modobserver @*/
1110         unsigned char c = *lexer->inbufptr++;
1111         /*@ +modobserver @*/
1112         char *state_table[] = {
1113 #include "packet_names.h"
1114         };
1115         nextstate(lexer, c);
1116         gpsd_report(LOG_RAW + 2,
1117                     "%08ld: character '%c' [%02x], new state: %s\n",
1118                     lexer->char_counter, (isprint(c) ? c : '.'), c,
1119                     state_table[lexer->state]);
1120         lexer->char_counter++;
1121
1122         if (lexer->state == GROUND_STATE) {
1123             character_discard(lexer);
1124         } else if (lexer->state == COMMENT_RECOGNIZED) {
1125             packet_accept(lexer, COMMENT_PACKET);
1126             packet_discard(lexer);
1127             lexer->state = GROUND_STATE;
1128             break;
1129         }
1130 #ifdef NMEA_ENABLE
1131         else if (lexer->state == NMEA_RECOGNIZED) {
1132             bool checksum_ok = true;
1133             char csum[3] = { '0', '0', '0' };
1134             char *end;
1135             /*
1136              * Back up past any whitespace.  Need to do this because
1137              * at least one GPS (the Firefly 1a) emits \r\r\n
1138              */
1139             for (end = (char *)lexer->inbufptr - 1; isspace(*end); end--)
1140                 continue;
1141             while (strchr("0123456789ABCDEF", *end))
1142                 --end;
1143             if (*end == '*') {
1144                 unsigned int n, crc = 0;
1145                 for (n = 1; (char *)lexer->inbuffer + n < end; n++)
1146                     crc ^= lexer->inbuffer[n];
1147                 (void)snprintf(csum, sizeof(csum), "%02X", crc);
1148                 checksum_ok = (csum[0] == toupper(end[1])
1149                                && csum[1] == toupper(end[2]));
1150             }
1151             if (checksum_ok) {
1152 #ifdef AIVDM_ENABLE
1153                 if (strncmp((char *)lexer->inbuffer, "!AIVDM", 6) == 0)
1154                     packet_accept(lexer, AIVDM_PACKET);
1155                 else
1156 #endif /* AIVDM_ENABLE */
1157                     packet_accept(lexer, NMEA_PACKET);
1158             } else {
1159                 gpsd_report(LOG_WARN,
1160                             "bad checksum in NMEA packet; expected %s.\n",
1161                             csum);
1162                 packet_accept(lexer, BAD_PACKET);
1163                 lexer->state = GROUND_STATE;
1164             }
1165             packet_discard(lexer);
1166             break;
1167         }
1168 #endif /* NMEA_ENABLE */
1169 #ifdef SIRF_ENABLE
1170         else if (lexer->state == SIRF_RECOGNIZED) {
1171             unsigned char *trailer = lexer->inbufptr - 4;
1172             unsigned int checksum =
1173                 (unsigned)((trailer[0] << 8) | trailer[1]);
1174             unsigned int n, crc = 0;
1175             for (n = 4; n < (unsigned)(trailer - lexer->inbuffer); n++)
1176                 crc += (int)lexer->inbuffer[n];
1177             crc &= 0x7fff;
1178             if (checksum == crc)
1179                 packet_accept(lexer, SIRF_PACKET);
1180             else {
1181                 packet_accept(lexer, BAD_PACKET);
1182                 lexer->state = GROUND_STATE;
1183             }
1184             packet_discard(lexer);
1185             break;
1186         }
1187 #endif /* SIRF_ENABLE */
1188 #ifdef SUPERSTAR2_ENABLE
1189         else if (lexer->state == SUPERSTAR2_RECOGNIZED) {
1190             unsigned a = 0, b;
1191             size_t n;
1192             lexer->length = 4 + (size_t) lexer->inbuffer[3] + 2;
1193             for (n = 0; n < lexer->length - 2; n++)
1194                 a += (unsigned)lexer->inbuffer[n];
1195             b = (unsigned)getleuw(lexer->inbuffer, lexer->length - 2);
1196             gpsd_report(LOG_IO, "SuperStarII pkt dump: type %u len %u: %s\n",
1197                         lexer->inbuffer[1], (unsigned int)lexer->length,
1198                         gpsd_hexdump_wrapper(lexer->inbuffer, lexer->length,
1199                                              LOG_RAW));
1200             if (a != b) {
1201                 gpsd_report(LOG_IO, "REJECT SuperStarII packet type 0x%02x"
1202                             "%zd bad checksum 0x%04x, expecting 0x%04x\n",
1203                             lexer->inbuffer[1], lexer->length, a, b);
1204                 packet_accept(lexer, BAD_PACKET);
1205                 lexer->state = GROUND_STATE;
1206             } else {
1207                 packet_accept(lexer, SUPERSTAR2_PACKET);
1208             }
1209             packet_discard(lexer);
1210             break;
1211         }
1212 #endif /* SUPERSTAR2_ENABLE */
1213 #ifdef ONCORE_ENABLE
1214         else if (lexer->state == ONCORE_RECOGNIZED) {
1215             char a, b;
1216             int i, len;
1217
1218             len = lexer->inbufptr - lexer->inbuffer;
1219             a = (char)(lexer->inbuffer[len - 3]);
1220             b = '\0';
1221             for (i = 2; i < len - 3; i++)
1222                 b ^= lexer->inbuffer[i];
1223             if (a == b) {
1224                 gpsd_report(LOG_IO, "Accept OnCore packet @@%c%c len %d\n",
1225                             lexer->inbuffer[2], lexer->inbuffer[3], len);
1226                 packet_accept(lexer, ONCORE_PACKET);
1227             } else {
1228                 gpsd_report(LOG_IO, "REJECT OnCore packet @@%c%c len %d\n",
1229                             lexer->inbuffer[2], lexer->inbuffer[3], len);
1230                 packet_accept(lexer, BAD_PACKET);
1231                 lexer->state = GROUND_STATE;
1232             }
1233             packet_discard(lexer);
1234             break;
1235         }
1236 #endif /* ONCORE_ENABLE */
1237 #if defined(TSIP_ENABLE) || defined(GARMIN_ENABLE)
1238         else if (lexer->state == TSIP_RECOGNIZED) {
1239             size_t packetlen = lexer->inbufptr - lexer->inbuffer;
1240 #ifdef TSIP_ENABLE
1241             unsigned int pos, dlecnt;
1242             /* don't count stuffed DLEs in the length */
1243             dlecnt = 0;
1244             for (pos = 0; pos < (unsigned int)packetlen; pos++)
1245                 if (lexer->inbuffer[pos] == DLE)
1246                     dlecnt++;
1247             if (dlecnt > 2) {
1248                 dlecnt -= 2;
1249                 dlecnt /= 2;
1250                 gpsd_report(LOG_RAW, "Unstuffed %d DLEs\n", dlecnt);
1251                 packetlen -= dlecnt;
1252             }
1253 #endif /* TSIP_ENABLE */
1254             if (packetlen < 5) {
1255                 lexer->state = GROUND_STATE;
1256             } else {
1257                 unsigned int pkt_id, len;
1258                 size_t n;
1259 #ifdef GARMIN_ENABLE
1260                 unsigned int ch, chksum;
1261                 n = 0;
1262                 /*@ +charint */
1263 #ifdef TSIP_ENABLE
1264                 /* shortcut garmin */
1265                 if (TSIP_PACKET == lexer->type)
1266                     goto not_garmin;
1267 #endif /* TSIP_ENABLE */
1268                 if (lexer->inbuffer[n++] != DLE)
1269                     goto not_garmin;
1270                 pkt_id = lexer->inbuffer[n++];  /* packet ID */
1271                 len = lexer->inbuffer[n++];
1272                 chksum = len + pkt_id;
1273                 if (len == DLE) {
1274                     if (lexer->inbuffer[n++] != DLE)
1275                         goto not_garmin;
1276                 }
1277                 for (; len > 0; len--) {
1278                     chksum += lexer->inbuffer[n];
1279                     if (lexer->inbuffer[n++] == DLE) {
1280                         if (lexer->inbuffer[n++] != DLE)
1281                             goto not_garmin;
1282                     }
1283                 }
1284                 /* check sum byte */
1285                 ch = lexer->inbuffer[n++];
1286                 chksum += ch;
1287                 if (ch == DLE) {
1288                     if (lexer->inbuffer[n++] != DLE)
1289                         goto not_garmin;
1290                 }
1291                 if (lexer->inbuffer[n++] != DLE)
1292                     goto not_garmin;
1293                 if (lexer->inbuffer[n++] != ETX)
1294                     goto not_garmin;
1295                 /*@ +charint */
1296                 chksum &= 0xff;
1297                 if (chksum) {
1298                     gpsd_report(LOG_IO,
1299                                 "Garmin checksum failed: %02x!=0\n", chksum);
1300                     goto not_garmin;
1301                 }
1302                 /* Debug
1303                  * gpsd_report(LOG_IO, "Garmin n= %#02x\n %s\n", n,
1304                  * gpsd_hexdump_wrapper(lexer->inbuffer, packetlen, LOG_IO));
1305                  */
1306                 packet_accept(lexer, GARMIN_PACKET);
1307                 packet_discard(lexer);
1308                 break;
1309               not_garmin:;
1310                 gpsd_report(LOG_RAW + 1, "Not a Garmin packet\n");
1311 #endif /* GARMIN_ENABLE */
1312 #ifdef TSIP_ENABLE
1313                 /* check for some common TSIP packet types:
1314                  * 0x13, TSIP Parsing Error Notification
1315                  * 0x41, GPS time, data length 10
1316                  * 0x42, Single Precision Fix, data length 16
1317                  * 0x43, Velocity Fix, data length 20
1318                  * 0x45, Software Version Information, data length 10
1319                  * 0x46, Health of Receiver, data length 2
1320                  * 0x48, GPS System Messages
1321                  * 0x49, Almanac Health Page
1322                  * 0x4a, LLA Position, data length 20
1323                  * 0x4b, Machine Code Status, data length 3
1324                  * 0x4c, Operating Parameters Report
1325                  * 0x54, One Satellite Bias
1326                  * 0x56, Velocity Fix (ENU), data length 20
1327                  * 0x57, Last Computed Fix Report
1328                  * 0x5a, Raw Measurements
1329                  * 0x5b, Satellite Ephemeris Status
1330                  * 0x5c, Satellite Tracking Status, data length 24
1331                  * 0x5e, Additional Fix Status Report
1332                  * 0x6d, All-In-View Satellite Selection, data length 16+numSV
1333                  * 0x82, Differential Position Fix Mode, data length 1
1334                  * 0x83, Double Precision XYZ, data length 36
1335                  * 0x84, Double Precision LLA, data length 36
1336                  * 0xbb, GPS Navigation Configuration
1337                  * 0xbc, Receiver Port Configuration
1338                  *
1339                  * <DLE>[pkt id] [data] <DLE><ETX>
1340                  */
1341                 /*@ +charint @*/
1342                 pkt_id = lexer->inbuffer[1];    /* packet ID */
1343                 /* *INDENT-OFF* */
1344                 if (!((0x13 == pkt_id) || (0xbb == pkt_id) || (0xbc == pkt_id))
1345                     && ((0x41 > pkt_id) || (0x8f < pkt_id))) {
1346                     gpsd_report(LOG_IO,
1347                                 "Packet ID 0x%02x out of range for TSIP\n",
1348                                 pkt_id);
1349                     goto not_tsip;
1350                 }
1351                 /* *INDENT-ON* */
1352                 /*@ -ifempty */
1353                 if ((0x13 == pkt_id) && (0x01 <= packetlen))
1354                     /* pass */ ;
1355                 else if ((0x41 == pkt_id)
1356                          && ((0x0e == packetlen) || (0x0f == packetlen)))
1357                     /* pass */ ;
1358                 else if ((0x42 == pkt_id) && (0x14 == packetlen))
1359                     /* pass */ ;
1360                 else if ((0x43 == pkt_id) && (0x18 == packetlen))
1361                     /* pass */ ;
1362                 else if ((0x45 == pkt_id) && (0x0e == packetlen))
1363                     /* pass */ ;
1364                 else if ((0x46 == pkt_id) && (0x06 == packetlen))
1365                     /* pass */ ;
1366                 else if ((0x48 == pkt_id) && (0x1a == packetlen))
1367                     /* pass */ ;
1368                 else if ((0x49 == pkt_id) && (0x24 == packetlen))
1369                     /* pass */ ;
1370                 else if ((0x4a == pkt_id) && (0x18 == packetlen))
1371                     /* pass */ ;
1372                 else if ((0x4b == pkt_id) && (0x07 == packetlen))
1373                     /* pass */ ;
1374                 else if ((0x4c == pkt_id) && (0x15 == packetlen))
1375                     /* pass */ ;
1376                 else if ((0x54 == pkt_id) && (0x10 == packetlen))
1377                     /* pass */ ;
1378                 else if ((0x55 == pkt_id) && (0x08 == packetlen))
1379                     /* pass */ ;
1380                 else if ((0x56 == pkt_id) && (0x18 == packetlen))
1381                     /* pass */ ;
1382                 else if ((0x57 == pkt_id) && (0x0c == packetlen))
1383                     /* pass */ ;
1384                 else if ((0x5a == pkt_id)
1385                          && ((0x1d <= packetlen) && (0x1f >= packetlen)))
1386                     /* pass */ ;
1387                 else if ((0x5b == pkt_id) && (0x24 == packetlen))
1388                     /* pass */ ;
1389                 else if ((0x5c == pkt_id)
1390                          && ((0x1c <= packetlen) && (0x1e >= packetlen)))
1391                     /* pass */ ;
1392                 else if ((0x5e == pkt_id) && (0x06 == packetlen))
1393                     /* pass */ ;
1394                 else if ((0x5f == pkt_id) && (70 == packetlen))
1395                     /* pass */ ;
1396                 else if ((0x6d == pkt_id)
1397                          && ((0x14 <= packetlen) && (0x20 >= packetlen)))
1398                     /* pass */ ;
1399                 else if ((0x82 == pkt_id) && (0x05 == packetlen))
1400                     /* pass */ ;
1401                 else if ((0x84 == pkt_id)
1402                          && ((0x28 <= packetlen) && (0x29 >= packetlen)))
1403                     /* pass */ ;
1404                 else if ((0x8e == pkt_id))
1405                     /* pass */ ;
1406                 else if ((0x8f == pkt_id))
1407                     /* pass */ ;
1408                 else if ((0xbb == pkt_id) && (0x2c == packetlen))
1409                     /* pass */ ;
1410                 else {
1411                     gpsd_report(LOG_IO,
1412                                 "TSIP REJECT pkt_id = %#02x, packetlen= %zu\n",
1413                                 pkt_id, packetlen);
1414                     goto not_tsip;
1415                 }
1416                 /* Debug */
1417                 gpsd_report(LOG_RAW,
1418                             "TSIP pkt_id = %#02x, packetlen= %zu\n",
1419                             pkt_id, packetlen);
1420                 /*@ -charint +ifempty @*/
1421                 packet_accept(lexer, TSIP_PACKET);
1422                 packet_discard(lexer);
1423                 break;
1424               not_tsip:
1425                 gpsd_report(LOG_RAW + 1, "Not a TSIP packet\n");
1426                 /*
1427                  * More attempts to recognize ambiguous TSIP-like
1428                  * packet types could go here.
1429                  */
1430                 packet_accept(lexer, BAD_PACKET);
1431                 lexer->state = GROUND_STATE;
1432                 packet_discard(lexer);
1433                 break;
1434 #endif /* TSIP_ENABLE */
1435             }
1436         }
1437 #endif /* TSIP_ENABLE || GARMIN_ENABLE */
1438 #ifdef RTCM104V3_ENABLE
1439         else if (lexer->state == RTCM3_RECOGNIZED) {
1440             if (crc24q_check(lexer->inbuffer,
1441                              lexer->inbufptr - lexer->inbuffer)) {
1442                 packet_accept(lexer, RTCM3_PACKET);
1443                 packet_discard(lexer);
1444             } else {
1445                 gpsd_report(LOG_IO, "RTCM3 data checksum failure, "
1446                             "%0x against %02x %02x %02x\n",
1447                             crc24q_hash(lexer->inbuffer,
1448                                         lexer->inbufptr - lexer->inbuffer -
1449                                         3), lexer->inbufptr[-3],
1450                             lexer->inbufptr[-2], lexer->inbufptr[-1]);
1451                 packet_accept(lexer, BAD_PACKET);
1452                 lexer->state = GROUND_STATE;
1453                 packet_discard(lexer);
1454             }
1455             break;
1456         }
1457 #endif /* RTCM104V3_ENABLE */
1458 #ifdef ZODIAC_ENABLE
1459         else if (lexer->state == ZODIAC_RECOGNIZED) {
1460             short len, n, sum;
1461             len = getword(2);
1462             for (n = sum = 0; n < len; n++)
1463                 sum += getword(5 + n);
1464             sum *= -1;
1465             if (len == 0 || sum == getword(5 + len)) {
1466                 packet_accept(lexer, ZODIAC_PACKET);
1467             } else {
1468                 gpsd_report(LOG_IO,
1469                             "Zodiac data checksum 0x%hx over length %hd, expecting 0x%hx\n",
1470                             sum, len, getword(5 + len));
1471                 packet_accept(lexer, BAD_PACKET);
1472                 lexer->state = GROUND_STATE;
1473             }
1474             packet_discard(lexer);
1475             break;
1476         }
1477 #endif /* ZODIAC_ENABLE */
1478 #ifdef UBX_ENABLE
1479         else if (lexer->state == UBX_RECOGNIZED) {
1480             /* UBX use a TCP like checksum */
1481             int n, len;
1482             unsigned char ck_a = (unsigned char)0;
1483             unsigned char ck_b = (unsigned char)0;
1484             len = lexer->inbufptr - lexer->inbuffer;
1485             gpsd_report(LOG_IO, "UBX: len %d\n", len);
1486             for (n = 2; n < (len - 2); n++) {
1487                 ck_a += lexer->inbuffer[n];
1488                 ck_b += ck_a;
1489             }
1490             if (ck_a == lexer->inbuffer[len - 2] &&
1491                 ck_b == lexer->inbuffer[len - 1])
1492                 packet_accept(lexer, UBX_PACKET);
1493             else {
1494                 gpsd_report(LOG_IO,
1495                             "UBX checksum 0x%02hhx%02hhx over length %hd,"
1496                             " expecting 0x%02hhx%02hhx (type 0x%02hhx%02hhx)\n",
1497                             ck_a,
1498                             ck_b,
1499                             len,
1500                             lexer->inbuffer[len - 2],
1501                             lexer->inbuffer[len - 1],
1502                             lexer->inbuffer[2], lexer->inbuffer[3]);
1503                 packet_accept(lexer, BAD_PACKET);
1504                 lexer->state = GROUND_STATE;
1505             }
1506             packet_discard(lexer);
1507             break;
1508         }
1509 #endif /* UBX_ENABLE */
1510 #ifdef EVERMORE_ENABLE
1511         else if (lexer->state == EVERMORE_RECOGNIZED) {
1512             unsigned int n, crc, checksum, len;
1513             n = 0;
1514             /*@ +charint */
1515             if (lexer->inbuffer[n++] != DLE)
1516                 goto not_evermore;
1517             if (lexer->inbuffer[n++] != STX)
1518                 goto not_evermore;
1519             len = lexer->inbuffer[n++];
1520             if (len == DLE) {
1521                 if (lexer->inbuffer[n++] != DLE)
1522                     goto not_evermore;
1523             }
1524             len -= 2;
1525             crc = 0;
1526             for (; len > 0; len--) {
1527                 crc += lexer->inbuffer[n];
1528                 if (lexer->inbuffer[n++] == DLE) {
1529                     if (lexer->inbuffer[n++] != DLE)
1530                         goto not_evermore;
1531                 }
1532             }
1533             checksum = lexer->inbuffer[n++];
1534             if (checksum == DLE) {
1535                 if (lexer->inbuffer[n++] != DLE)
1536                     goto not_evermore;
1537             }
1538             if (lexer->inbuffer[n++] != DLE)
1539                 goto not_evermore;
1540             if (lexer->inbuffer[n++] != ETX)
1541                 goto not_evermore;
1542             crc &= 0xff;
1543             if (crc != checksum) {
1544                 gpsd_report(LOG_IO,
1545                             "EverMore checksum failed: %02x != %02x\n",
1546                             crc, checksum);
1547                 goto not_evermore;
1548             }
1549             /*@ +charint */
1550             packet_accept(lexer, EVERMORE_PACKET);
1551             packet_discard(lexer);
1552             break;
1553           not_evermore:
1554             packet_accept(lexer, BAD_PACKET);
1555             lexer->state = GROUND_STATE;
1556             packet_discard(lexer);
1557             break;
1558         }
1559 #endif /* EVERMORE_ENABLE */
1560 /* XXX CSK */
1561 #ifdef ITRAX_ENABLE
1562 #define getib(j) ((uint8_t)lexer->inbuffer[(j)])
1563 #define getiw(i) ((uint16_t)(((uint16_t)getib((i)+1) << 8) | (uint16_t)getib((i))))
1564
1565         else if (lexer->state == ITALK_RECOGNIZED) {
1566             volatile uint16_t len, n, csum, xsum, tmpw;
1567             volatile uint32_t tmpdw;
1568
1569             /* number of words */
1570             len = (uint16_t) (lexer->inbuffer[6] & 0xff);
1571
1572             /*@ -type @*/
1573             /* initialize all my registers */
1574             csum = tmpw = tmpdw = 0;
1575             /* expected checksum */
1576             xsum = getiw(7 + 2 * len);
1577
1578             for (n = 0; n < len; n++) {
1579                 tmpw = getiw(7 + 2 * n);
1580                 tmpdw = (csum + 1) * (tmpw + n);
1581                 csum ^= (tmpdw & 0xffff) ^ ((tmpdw >> 16) & 0xffff);
1582             }
1583             /*@ +type @*/
1584             if (len == 0 || csum == xsum)
1585                 packet_accept(lexer, ITALK_PACKET);
1586             else {
1587                 gpsd_report(LOG_IO,
1588                             "ITALK: checksum failed - "
1589                             "type 0x%02x expected 0x%04x got 0x%04x\n",
1590                             lexer->inbuffer[4], xsum, csum);
1591                 packet_accept(lexer, BAD_PACKET);
1592                 lexer->state = GROUND_STATE;
1593             }
1594             packet_discard(lexer);
1595             break;
1596         }
1597 #undef getiw
1598 #undef getib
1599 #endif /* ITRAX_ENABLE */
1600 #ifdef NAVCOM_ENABLE
1601         else if (lexer->state == NAVCOM_RECOGNIZED) {
1602             /* By the time we got here we know checksum is OK */
1603             packet_accept(lexer, NAVCOM_PACKET);
1604             packet_discard(lexer);
1605             break;
1606         }
1607 #endif /* NAVCOM_ENABLE */
1608 #ifdef RTCM104V2_ENABLE
1609         else if (lexer->state == RTCM2_RECOGNIZED) {
1610             /*
1611              * RTCM packets don't have checksums.  The six bits of parity
1612              * per word and the preamble better be good enough.
1613              */
1614             packet_accept(lexer, RTCM2_PACKET);
1615             lexer->state = RTCM2_SYNC_STATE;
1616             packet_discard(lexer);
1617             break;
1618         }
1619 #endif /* RTCM104V2_ENABLE */
1620 #ifdef GARMINTXT_ENABLE
1621         else if (lexer->state == GTXT_RECOGNIZED) {
1622             size_t packetlen = lexer->inbufptr - lexer->inbuffer;
1623             if (57 <= packetlen) {
1624                 packet_accept(lexer, GARMINTXT_PACKET);
1625                 packet_discard(lexer);
1626                 lexer->state = GROUND_STATE;
1627                 break;
1628             } else {
1629                 packet_accept(lexer, BAD_PACKET);
1630                 lexer->state = GROUND_STATE;
1631             }
1632         }
1633 #endif
1634     }                           /* while */
1635 }
1636
1637 #undef getword
1638
1639 ssize_t packet_get(int fd, struct gps_packet_t *lexer)
1640 /* grab a packet; return -1=>I/O error, 0=>EOF, BAD_PACKET or a length */
1641 {
1642     ssize_t recvd;
1643
1644     /*@ -modobserver @*/
1645     errno = 0;
1646     recvd = read(fd, lexer->inbuffer + lexer->inbuflen,
1647                  sizeof(lexer->inbuffer) - (lexer->inbuflen));
1648     /*@ +modobserver @*/
1649     if (recvd == -1) {
1650         if ((errno == EAGAIN) || (errno == EINTR)) {
1651 #ifdef STATE_DEBUG
1652             gpsd_report(LOG_RAW + 2, "no bytes ready\n");
1653             recvd = 0;
1654             /* fall through, input buffer may be nonempty */
1655 #endif /* STATE_DEBUG */
1656         } else {
1657 #ifdef STATE_DEBUG
1658             gpsd_report(LOG_RAW + 2, "errno: %s\n", strerror(errno));
1659 #endif /* STATE_DEBUG */
1660             return -1;
1661         }
1662     } else {
1663 #ifdef STATE_DEBUG
1664         gpsd_report(LOG_RAW + 1,
1665                     "Read %zd chars to buffer offset %zd (total %zd): %s\n",
1666                     recvd, lexer->inbuflen, lexer->inbuflen + recvd,
1667                     gpsd_hexdump_wrapper(lexer->inbufptr, (size_t) recvd,
1668                                          LOG_RAW + 1));
1669 #endif /* STATE_DEBUG */
1670         lexer->inbuflen += recvd;
1671     }
1672     gpsd_report(LOG_SPIN, "packet_get() fd %d -> %zd (%d)\n",
1673                 fd, recvd, errno);
1674
1675     /*
1676      * Bail out, indicating no more input, only if we just received
1677      * nothing from the device and there is nothing waiting in the 
1678      * packet input buffer.  
1679      */
1680     if (recvd <= 0 && packet_buffered_input(lexer) <= 0)
1681         return recvd;
1682
1683     /* Otherwise, consume from the packet input buffer */
1684     packet_parse(lexer);
1685
1686     /* if input buffer is full, discard */
1687     if (sizeof(lexer->inbuffer) == (lexer->inbuflen)) {
1688         packet_discard(lexer);
1689         lexer->state = GROUND_STATE;
1690     }
1691
1692     /*
1693      * If we gathered a packet, return its length; it will have been
1694      * consumed out of the input buffer and moved to the output
1695      * buffer.  We don't care whether the read() returned 0 or -1 and
1696      * gathered packet data was all buffered or whether ot was partly
1697      * just physically read.
1698      *
1699      * Note: this choice greatly simplifies life for callers of
1700      * packet_get(), but means that they cannot tell when a nonzero
1701      * return means there was a successful physical read.  They will
1702      * thus credit a data source that drops out with being alive 
1703      * slightly longer than it actually was.  This is unlikely to
1704      * matter as long as any policy timeouts are large compared to
1705      * the time required to consume the greatest possible amount 
1706      * of buffered input, but if you hack this code you need to
1707      * be aware of the issue. It might also slightly affect 
1708      * performance profiling.
1709      */
1710     if (lexer->outbuflen > 0)
1711         return (ssize_t) lexer->outbuflen;
1712     else
1713         /*
1714          * Otherwise recvd is the size of whatever packet fragment we got.
1715          * It can still be 0 or -1 at this point even if buffer data 
1716          * was consumed.
1717          */
1718         return recvd;
1719 }
1720
1721 void packet_reset( /*@out@*/ struct gps_packet_t *lexer)
1722 /* return the packet machine to the ground state */
1723 {
1724     lexer->type = BAD_PACKET;
1725     lexer->state = GROUND_STATE;
1726     lexer->inbuflen = 0;
1727     lexer->inbufptr = lexer->inbuffer;
1728 #ifdef BINARY_ENABLE
1729     isgps_init(lexer);
1730 #endif /* BINARY_ENABLE */
1731 }
1732
1733
1734 #ifdef __UNUSED__
1735 void packet_pushback(struct gps_packet_t *lexer)
1736 /* push back the last packet grabbed */
1737 {
1738     if (lexer->outbuflen + lexer->inbuflen < MAX_PACKET_LENGTH) {
1739         memmove(lexer->inbuffer + lexer->outbuflen,
1740                 lexer->inbuffer, lexer->inbuflen);
1741         memmove(lexer->inbuffer, lexer->outbuffer, lexer->outbuflen);
1742         lexer->inbuflen += lexer->outbuflen;
1743         lexer->inbufptr += lexer->outbuflen;
1744         lexer->outbuflen = 0;
1745     }
1746 }
1747 #endif /* __UNUSED */
1748
1749 #ifdef ONCORE_ENABLE
1750 size_t oncore_payload_cksum_length(unsigned char id1, unsigned char id2)
1751 {
1752     size_t l;
1753
1754     /* For the packet sniffer to not terminate the message due to
1755      * payload data looking like a trailer, the known payload lengths
1756      * including the checksum are given.  Return -1 for unknown IDs.
1757      */
1758
1759 #define ONCTYPE(id2,id3) ((((unsigned int)id2)<<8)|(id3))
1760
1761     /* *INDENT-OFF* */
1762     switch (ONCTYPE(id1,id2)) {
1763     case ONCTYPE('A','b'): l = 10; break; /* GMT offset */
1764     case ONCTYPE('A','w'): l =  8; break; /* time mode */
1765     case ONCTYPE('A','c'): l = 11; break; /* date */
1766     case ONCTYPE('A','a'): l = 10; break; /* time of day */
1767     case ONCTYPE('A','d'): l = 11; break; /* latitude */
1768     case ONCTYPE('A','e'): l = 11; break; /* longitude */
1769     case ONCTYPE('A','f'): l = 15; break; /* height */
1770     case ONCTYPE('E','a'): l = 76; break; /* position/status/data */
1771     case ONCTYPE('A','g'): l =  8; break; /* satellite mask angle */
1772     case ONCTYPE('B','b'): l = 92; break; /* visible satellites status */
1773     case ONCTYPE('B','j'): l =  8; break; /* leap seconds pending */
1774     case ONCTYPE('A','q'): l =  8; break; /* atmospheric correction mode */
1775     case ONCTYPE('A','p'): l = 25; break; /* set user datum / select datum */
1776     /* Command "Ao" gives "Ap" response   (select datum) */
1777     case ONCTYPE('C','h'): l =  9; break; /* almanac input ("Cb" response) */
1778     case ONCTYPE('C','b'): l = 33; break; /* almanac output ("Be" response) */
1779     case ONCTYPE('S','z'): l =  8; break; /* system power-on failure */
1780     case ONCTYPE('C','j'): l = 294; break; /* receiver ID */
1781     case ONCTYPE('F','a'): l =  9; break; /* self-test */
1782     case ONCTYPE('C','f'): l =  7; break; /* set-to-defaults */
1783     case ONCTYPE('E','q'): l = 96; break; /* ASCII position */
1784     case ONCTYPE('A','u'): l = 12; break; /* altitide hold height */
1785     case ONCTYPE('A','v'): l =  8; break; /* altitude hold mode */
1786     case ONCTYPE('A','N'): l =  8; break; /* velocity filter */
1787     case ONCTYPE('A','O'): l =  8; break; /* RTCM report mode */
1788     case ONCTYPE('C','c'): l = 80; break; /* ephemeris data input ("Bf") */
1789     case ONCTYPE('C','k'): l =  7; break; /* pseudorng correction inp. ("Ce")*/
1790     /* Command "Ci" (switch to NMEA, GT versions only) has no response */
1791     case ONCTYPE('B','o'): l =  8; break; /* UTC offset status */
1792     case ONCTYPE('A','z'): l = 11; break; /* 1PPS cable delay */
1793     case ONCTYPE('A','y'): l = 11; break; /* 1PPS offset */
1794     case ONCTYPE('A','P'): l =  8; break; /* pulse mode */
1795     case ONCTYPE('A','s'): l = 20; break; /* position-hold position */
1796     case ONCTYPE('A','t'): l =  8; break; /* position-hold mode */
1797     case ONCTYPE('E','n'): l = 69; break; /* time RAIM setup and status */
1798     default:
1799         return 0;
1800     }
1801     /* *INDENT-ON* */
1802
1803     return l - 6;               /* Subtract header and trailer. */
1804 }
1805 #endif /* ONCORE_ENABLE */