diff options
author | Gary E. Miller <gem@rellim.com> | 2006-10-30 02:21:11 +0000 |
---|---|---|
committer | Gary E. Miller <gem@rellim.com> | 2006-10-30 02:21:11 +0000 |
commit | 1eb8ef0dbf7d746f38b1987c8a73261c7cf0912f (patch) | |
tree | 7a2816f543181983d355ffed79be59b11f77671a /garmin.c | |
parent | bb1a554d74c9d443d085e756151f7c8a328ae6ba (diff) | |
download | gpsd-1eb8ef0dbf7d746f38b1987c8a73261c7cf0912f.tar.gz |
Convert garmin_usb to run in serial emulation mode...
instead of packet mode. A bit simpler than the old way and works
around a kernel bug.
Diffstat (limited to 'garmin.c')
-rw-r--r-- | garmin.c | 602 |
1 files changed, 312 insertions, 290 deletions
@@ -15,7 +15,8 @@ * modes. * * Protocol info from: - * GPS18_TechnicalSpecification.pdf + * 425_TechnicalSpecification.pdf + * ( formerly GPS18_TechnicalSpecification.pdf ) * iop_spec.pdf * http://www.garmin.com/support/commProtocol.html * @@ -67,6 +68,13 @@ #ifdef GARMIN_ENABLE +#define USE_RMD 0 + +#define ETX 0x03 +#define ACK 0x06 +#define DLE 0x10 +#define NAK 0x15 + #define GARMIN_LAYERID_TRANSPORT (uint8_t) 0 #define GARMIN_LAYERID_APPL (uint32_t) 20 // Linux Garmin USB driver layer-id to use for some control mechanisms @@ -89,8 +97,12 @@ #define GARMIN_PKTID_PROTOCOL_ARRAY 253 #define GARMIN_PKTID_PRODUCT_RQST 254 #define GARMIN_PKTID_PRODUCT_DATA 255 +/* 0x29 ')' */ +#define GARMIN_PKTID_RMD41_DATA 41 /* 0x33 '3' */ #define GARMIN_PKTID_PVT_DATA 51 +/* 0x33 '4' */ +#define GARMIN_PKTID_RMD_DATA 52 /* 0x72 'r' */ #define GARMIN_PKTID_SAT_DATA 114 @@ -127,8 +139,9 @@ typedef struct { // bit 3?? } cpo_sat_data; -/* Garmin D800_Pvt_Date_Type */ -// This is the data format of the position data from the garmin USB +/* Garmin D800_Pvt_Datetype_Type */ +/* packet type: GARMIN_PKTID_PVT_DATA 52 */ +/* This is the data format of the position data from the garmin USB */ typedef struct { float alt; /* altitude above WGS 84 (meters) */ float epe; /* estimated position error, 2 sigma (meters) */ @@ -147,28 +160,30 @@ typedef struct { float lon_vel; /* velocity east (meters/second) */ float lat_vel; /* velocity north (meters/second) */ float alt_vel; /* velocity up (meters/sec) */ + // Garmin GPS25 uses pkt_id 0x28 and does not output the + // next 3 items float msl_hght; /* height of WGS 84 above MSL (meters) */ int16_t leap_sec; /* diff between GPS and UTC (seconds) */ int32_t grmn_days; } cpo_pvt_data; -#ifdef __UNUSED__ typedef struct { uint32_t cycles; double pr; uint16_t phase; int8_t slp_dtct; uint8_t snr_dbhz; - int8_t svid; + uint8_t svid; int8_t valid; } cpo_rcv_sv_data; +/* packet type: GARMIN_PKTID_RMD_DATA 53 */ +/* seems identical to the packet id 0x29 from the Garmin GPS 25 */ typedef struct { double rcvr_tow; int16_t rcvr_wn; cpo_rcv_sv_data sv[GARMIN_CHANNELS]; } cpo_rcv_data; -#endif /* __UNUSED__ */ // This is the packet format to/from the Garmin USB typedef struct { @@ -223,7 +238,6 @@ static inline double radtodeg( double rad) { static gps_mask_t PrintSERPacket(struct gps_device_t *session, unsigned char pkt_id, int pkt_len, unsigned char *buf ); static gps_mask_t PrintUSBPacket(struct gps_device_t *session, Packet_t *pkt ); -static int GetPacket (struct gps_device_t *session ); gps_mask_t PrintSERPacket(struct gps_device_t *session, unsigned char pkt_id , int pkt_len, unsigned char *buf ) @@ -241,10 +255,17 @@ gps_mask_t PrintSERPacket(struct gps_device_t *session, unsigned char pkt_id char *msg = NULL; cpo_sat_data *sats = NULL; cpo_pvt_data *pvt = NULL; + cpo_rcv_data *rmd = NULL; gpsd_report(4, "PrintSERPacket(, %#02x, %#02x, )\n", pkt_id, pkt_len); switch( pkt_id ) { + case ACK: + gpsd_report(3, "ACK\n"); + break; + case NAK: + gpsd_report(3, "NAK\n"); + break; case GARMIN_PKTID_L001_COMMAND_DATA: prod_id = get_uint16((uint8_t *)buf); /*@ -branchstate @*/ @@ -391,15 +412,30 @@ gps_mask_t PrintSERPacket(struct gps_device_t *session, unsigned char pkt_id mask |= TIME_SET | LATLON_SET | ALTITUDE_SET | STATUS_SET | MODE_SET | SPEED_SET | TRACK_SET | CLIMB_SET | HERR_SET | VERR_SET | PERR_SET | CYCLE_START_SET; break; + case GARMIN_PKTID_RMD_DATA: + case GARMIN_PKTID_RMD41_DATA: + rmd = (cpo_rcv_data *) buf; + gpsd_report(4, "PVT RMD Data Sz: %d\n", pkt_len); + gpsd_report(3, "PVT RMD rcvr_tow: %f, rcvr_wn: %d\n" + , rmd->rcvr_tow, rmd->rcvr_wn); + for ( i = 0 ; i < GARMIN_CHANNELS ; i++ ) { + gpsd_report(3, "PVT RMD Sat: %3u, cycles: %9lu, pr: %16.6f, phase: %7.3f, slp_dtct: %3s, snr: %3u, Valid: %3s\n" + , rmd->sv[i].svid + 1, rmd->sv[i].cycles, rmd->sv[i].pr + , (rmd->sv[i].phase * 360.0)/2048.0 + , rmd->sv[i].slp_dtct ? "Yes" : "No" + , rmd->sv[i].snr_dbhz, rmd->sv[i].valid ? "Yes" : "No"); + } + break; + case GARMIN_PKTID_SAT_DATA: - gpsd_report(3, "Appl, SAT Data Sz: %d\n", pkt_len); + gpsd_report(3, "SAT Data Sz: %d\n", pkt_len); sats = (cpo_sat_data *)buf; session->gpsdata.satellites_used = 0; memset(session->gpsdata.used,0,sizeof(session->gpsdata.used)); gpsd_zero_satellites(&session->gpsdata); for ( i = 0, j = 0 ; i < GARMIN_CHANNELS ; i++, sats++ ) { - gpsd_report(4," Sat %d, snr: %d, elev: %d, Azmth: %d, Stat: %x\n" + gpsd_report(4," Sat %3d, snr: %5d, elev: %2d, Azmth: %3d, Stat: %x\n" , sats->svid , sats->snr , sats->elev @@ -439,8 +475,8 @@ gps_mask_t PrintSERPacket(struct gps_device_t *session, unsigned char pkt_id } break; default: - gpsd_report(3, "Appl, ID: %d, Sz: %d\n" - , pkt_id, pkt_len); + gpsd_report(3, "Unknown packet id: %#02x, Sz: %#02x, pkt:%s\n" + , pkt_id, pkt_len, gpsd_hexdump(buf, pkt_len)); break; } gpsd_report(3, "PrintSERPacket(, %#02x, %#02x, ) = %#02x\n" @@ -462,7 +498,17 @@ static gps_mask_t PrintUSBPacket(struct gps_device_t *session, Packet_t *pkt) uint32_t serial; uint32_t mDataSize = get_int32( (uint8_t*)&pkt->mDataSize); +// + uint8_t *buffer = (uint8_t*)pkt; + gpsd_report(3, "PrintUSBPacket()\n"); +// gem + if ( DLE == pkt->mPacketType) { + gpsd_report(3, "really a SER packet!\n"); + return PrintSERPacket ( session, buffer[1], buffer[2], buffer + 3); + } + +// gem if ( 4096 < mDataSize) { gpsd_report(3, "bogus packet, size too large=%d\n", mDataSize); return 0; @@ -542,8 +588,8 @@ static gps_mask_t PrintUSBPacket(struct gps_device_t *session, Packet_t *pkt) /*@ +branchstate @*/ -/* build and send a packet */ -static void Build_Send_Packet( struct gps_device_t *session, +/* build and send a packet w/ USB protocol */ +static void Build_Send_USB_Packet( struct gps_device_t *session, uint32_t layer_id, uint32_t pkt_id, uint32_t length, uint32_t data ) { uint8_t *buffer = (uint8_t *)session->driver.garmin.Buffer; @@ -584,137 +630,109 @@ static void Build_Send_Packet( struct gps_device_t *session, , &n, 0); } } - -//----------------------------------------------------------------------------- -// Gets a single packet. -// this is odd, the garmin usb driver will only return 64 bytes, or less -// at a time, no matter what you ask for. -// -// is you ask for less than 64 bytes then the next packet will include -// just the remaining bytes of the last 64 byte packet. -// -// Reading a packet of length Zero, or less than 64, signals the end of -// the entire packet. -// -// The Garmin sample WinXX code also assumes the same behavior, so -// maybe it is something in the USB protocol. -// -// Return: 0 = got a good packet -// -1 = error -// 1 = got partial packet -static int GetPacket (struct gps_device_t *session ) +/* build and send a packet in serial protocol */ +/* layer_id unused */ +static void Build_Send_SER_Packet( struct gps_device_t *session, + uint32_t layer_id, uint32_t pkt_id, uint32_t length, uint32_t data ) { - struct timespec delay, rem; - int cnt = 0; - // int x = 0; // for debug dump - - memset( session->driver.garmin.Buffer, 0, sizeof(Packet_t)); - memset( &delay, 0, sizeof(delay)); - session->driver.garmin.BufferLen = 0; - session->outbuflen = 0; - - gpsd_report(4, "GetPacket()\n"); - -//delay.tv_sec = 0; -//delay.tv_nsec = 33300000L; -//while (nanosleep(&delay, &rem) < 0) -// continue; - - for( cnt = 0 ; cnt < 10 ; cnt++ ) { - size_t pkt_size; - // Read async data until the driver returns less than the - // max async data size, which signifies the end of a packet - - // not optimal, but given the speed and packet nature of - // the USB not too bad for a start + uint8_t *buffer = (uint8_t *)session->driver.garmin.Buffer; + Packet_t *thePacket = (Packet_t*)buffer; ssize_t theBytesReturned = 0; - uint8_t *buf = (uint8_t *)session->driver.garmin.Buffer; - Packet_t *thePacket = (Packet_t*)buf; - - theBytesReturned = read(session->gpsdata.gps_fd - , buf + session->driver.garmin.BufferLen - , ASYNC_DATA_SIZE); - // zero byte returned is a legal value and denotes the end of a - // binary packet. - if ( 0 > theBytesReturned ) { - // read error... - // or EAGAIN, but O_NONBLOCK is never set - gpsd_report(0, "GetPacket() read error=%d, errno=%d\n" - , theBytesReturned, errno); - continue; - } - gpsd_report(5, "got %d bytes\n", theBytesReturned); - - session->driver.garmin.BufferLen += theBytesReturned; - if ( 256 <= session->driver.garmin.BufferLen ) { - // really bad read error... - gpsd_report(3, "GetPacket() packet too long, %ld > 255 !\n" - , session->driver.garmin.BufferLen); - session->driver.garmin.BufferLen = 0; - break; - } - pkt_size = 12 + get_int32((uint8_t*)&thePacket->mDataSize); - if ( 12 <= session->driver.garmin.BufferLen) { - // have enough data to check packet size - if ( session->driver.garmin.BufferLen > pkt_size) { - // wrong amount of data in buffer - gpsd_report(3 - , "GetPacket() packet size wrong! Packet: %ld, s/b %ld\n" - , session->driver.garmin.BufferLen - , pkt_size); - session->driver.garmin.BufferLen = 0; - break; - } - } - if ( 64 > theBytesReturned ) { - // zero length, or short, read is a flag for got the whole packet - break; + ssize_t theBytesToWrite = 6 + length; + uint8_t chksum = 0; + + *buffer++ = (uint8_t)DLE; + *buffer++ = (uint8_t)pkt_id; + chksum = pkt_id; + *buffer++ = (uint8_t)length; + chksum += length; + if ( 2 == length ) { + /* carefull! no DLE stuffing here! */ + set_int16(buffer, data); + chksum += buffer[0]; + chksum += buffer[1]; + } else if ( 4 == length ) { + /* carefull! no DLE stuffing here! */ + set_int32(buffer, data); + chksum += buffer[0]; + chksum += buffer[1]; + chksum += buffer[2]; + chksum += buffer[3]; } - + buffer += length; + + // Add checksum + *buffer++ = -chksum; + if ( DLE == -chksum ) { + /* stuff another DLE */ + *buffer++ = (uint8_t)DLE; + theBytesToWrite++; + } + + // Add DLE, ETX + *buffer++ = (uint8_t)DLE; + *buffer++ = (uint8_t)ETX; + +#if 1 + gpsd_report(4, "SendPacket(), writing %d bytes: %s\n" + , theBytesToWrite, gpsd_hexdump(thePacket, theBytesToWrite)); +#endif + (void)PrintSERPacket ( session, buffer[1], buffer[2], buffer + 3); - /*@ ignore @*/ - delay.tv_sec = 0; - delay.tv_nsec = 3330000L; - while (nanosleep(&delay, &rem) < 0) - continue; - /*@ end @*/ - } - // dump the individual bytes, debug only - // for ( x = 0; x < session->driver.garmin.BufferLen; x++ ) { - // gpsd_report(6, "p[%d] = %x\n", x, session->driver.garmin.Buffer[x]); - // } - if ( 10 <= cnt ) { - gpsd_report(3, "GetPacket() packet too long or too slow!\n"); - return -1; - } + theBytesReturned = write( session->gpsdata.gps_fd + , thePacket, theBytesToWrite); + gpsd_report(4, "SendPacket(), wrote %d bytes\n", theBytesReturned); - gpsd_report(5, "GotPacket() sz=%d \n", session->driver.garmin.BufferLen); - session->outbuflen = session->driver.garmin.BufferLen; - return 0; } + /* * garmin_probe() * + * check that the garmin_gps driver is installed in the kernel + * and that an active USB device is using it. + * + * It does not yet check that the currect device is the one + * attached to the garmin. So if you have a garmin and another + * gps this could be a problem. + * + * this is very linux specific. + * * return 1 if garmin_gps device found * return 0 if not + * */ static bool garmin_probe(struct gps_device_t *session) { - Packet_t *thePacket = NULL; - uint8_t *buffer = NULL; - fd_set fds, rfds; - struct timeval tv; - int sel_ret = 0; + FILE *fp = NULL; + char buf[256]; int ok = 0; - int i; - /* check for USB serial drivers -- very Linux-specific */ + /* check for garmin USB serial driver -- very Linux-specific */ if (access("/sys/module/garmin_gps", R_OK) != 0) { gpsd_report(5, "garmin_gps not active.\n"); return false; } + // check for a garmin_gps device in /proc + if ( !(fp = fopen( "/proc/bus/usb/devices", "r") ) ) { + gpsd_report(5, "Can't open /proc/bus/usb/devices\n"); + return false; + } + + ok = 0; + while ( 0 < fread( buf, sizeof(buf), 1, fp ) ) { + if ( strstr( buf, "garmin_gps") ) { + ok = 1; + break; + } + } + (void)fclose(fp); + if ( !ok ) { + // no device using garmin now + gpsd_report(5, "garmin_gps not in /proc/bus/usb/devices.\n"); + return false; + } /* Save original terminal parameters */ if (tcgetattr(session->gpsdata.gps_fd,&session->ttyset_old) != 0) { @@ -732,6 +750,9 @@ static bool garmin_probe(struct gps_device_t *session) return false; } +#ifdef __UNUSED + Packet_t *thePacket = NULL; + uint8_t *buffer = NULL; /* reset the buffer and buffer length */ memset( session->driver.garmin.Buffer, 0, sizeof(session->driver.garmin.Buffer) ); session->driver.garmin.BufferLen = 0; @@ -744,159 +765,16 @@ static bool garmin_probe(struct gps_device_t *session) buffer = (uint8_t *)session->driver.garmin.Buffer; thePacket = (Packet_t*)buffer; +#endif /* __UNUSED__ */ // set Mode 1, mode 0 is broken somewhere past 2.6.14 // but how? gpsd_report(3, "Set garmin_gps driver mode = 0\n"); - Build_Send_Packet( session, GARMIN_LAYERID_PRIVATE - , PRIV_PKTID_SET_MODE, 4, 0); + Build_Send_USB_Packet( session, GARMIN_LAYERID_PRIVATE + , PRIV_PKTID_SET_MODE, 4, MODE_GARMIN_SERIAL); // expect no return packet !? - // get Version info - gpsd_report(3, "Get garmin_gps driver version\n"); - Build_Send_Packet(session, GARMIN_LAYERID_PRIVATE, PRIV_PKTID_INFO_REQ - , 0, 0); - - /* get and print the driver Version info */ - - FD_ZERO(&fds); - FD_SET(session->gpsdata.gps_fd, &fds); - - /* Wait, nicely, until the device returns the Version info - * Toss any other packets, up to 4 */ - ok = 0; - memset( &tv,0,sizeof(tv)); - for( i = 0 ; i < 4 ; i++ ) { - memcpy((char *)&rfds, (char *)&fds, sizeof(rfds)); - - tv.tv_sec = 1; tv.tv_usec = 0; - sel_ret = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); - if (sel_ret < 0) { - if (errno == EINTR) - continue; - gpsd_report(0, "select: %s\n", strerror(errno)); - return false; - } else if ( sel_ret == 0 ) { - gpsd_report(3, "garmin_probe() timeout, INFO_REQ\n"); - // restore old terminal settings - // TCIOFLUSH here causes gpsfake to hang, so skip that - (void)tcsetattr(session->gpsdata.gps_fd, TCSANOW - , &session->ttyset_old); - return false; - } - if ( 0 == GetPacket( session ) ) { - (void)PrintUSBPacket(session, thePacket); - - if( ( (uint8_t)75 == thePacket->mPacketType) - && (PRIV_PKTID_INFO_RESP == thePacket->mPacketId) ) { - ok = 1; - break; - } - } - } - - if ( 0 == ok ) { - gpsd_report(2, "Garmin driver never answered to INFO_REQ.\n"); - // restore old terminal settings - (void)tcsetattr(session->gpsdata.gps_fd, TCIOFLUSH - , &session->ttyset_old); - return false; - } - /* depending on the GARMIN version, the device may spontaneously - return the Product Capability here */ - - /* Tell the device that we are starting a session. */ - gpsd_report(3, "Send Garmin Start Session\n"); - - Build_Send_Packet(session, GARMIN_LAYERID_TRANSPORT - , GARMIN_PKTID_TRANSPORT_START_SESSION_REQ, 0, 0); - - /* Wait until the device is ready to the start the session - * Toss any other packets, up to 4 */ - ok = 0; - for( i = 0 ; i < 4 ; i++ ) { - memcpy((char *)&rfds, (char *)&fds, sizeof(rfds)); - - tv.tv_sec = 1; tv.tv_usec = 0; - sel_ret = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); - if (sel_ret < 0) { - if (errno == EINTR) - continue; - gpsd_report(0, "select: %s\n", strerror(errno)); - return(0); - } else if ( sel_ret == 0 ) { - gpsd_report(3, "garmin_probe() timeout, START_SESSION\n"); - // restore old terminal settings - (void)tcsetattr(session->gpsdata.gps_fd, TCIOFLUSH - , &session->ttyset_old); - return(0); - } - if ( 0 == GetPacket( session ) ) { - gpsd_report(3, "Got packet waiting for START_SESSION\n"); - (void)PrintUSBPacket(session, thePacket); - - if( (GARMIN_LAYERID_TRANSPORT == thePacket->mPacketType) - && (GARMIN_PKTID_TRANSPORT_START_SESSION_RESP - == thePacket->mPacketId) ) { - ok = 1; - break; - } - } - } - - if ( 0 == ok ) { - gpsd_report(2, "Garmin driver never answered to START_SESSION.\n"); - // restore old terminal settings - (void)tcsetattr(session->gpsdata.gps_fd, TCIOFLUSH - , &session->ttyset_old); - return false; - } - - // Tell the device to send product data - gpsd_report(3, "Get Garmin Product Data\n"); - - Build_Send_Packet(session, GARMIN_LAYERID_APPL - , GARMIN_PKTID_PRODUCT_RQST, 0, 0); - - // Get the product data packet - // Toss any other packets, up to 4 - ok = 0; - for( i = 0 ; i < 4 ; i++ ) { - memcpy((char *)&rfds, (char *)&fds, sizeof(rfds)); - - tv.tv_sec = 1; tv.tv_usec = 0; - sel_ret = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); - if (sel_ret < 0) { - if (errno == EINTR) - continue; - gpsd_report(0, "select: %s\n", strerror(errno)); - return false; - } else if ( sel_ret == 0 ) { - gpsd_report(3, "garmin_probe() timeout, PRODUCT_DATA\n"); - // restore old terminal settings - (void)tcsetattr(session->gpsdata.gps_fd, TCIOFLUSH - , &session->ttyset_old); - return false; - } - if ( 0 == GetPacket( session ) ) { - (void)PrintUSBPacket(session, thePacket); - - if( (GARMIN_LAYERID_APPL == (uint32_t)thePacket->mPacketType) - && ( GARMIN_PKTID_PRODUCT_DATA == thePacket->mPacketId) ) { - ok = 1; - break; - } - } - } - - if ( 0 == ok ) { - gpsd_report(2, "Garmin driver never answered to PRODUCT_DATA.\n"); - // restore old terminal settings - (void)tcsetattr(session->gpsdata.gps_fd, TCIOFLUSH - , &session->ttyset_old); - return false; - } - return true; + return 1; } /* @@ -911,23 +789,23 @@ static bool garmin_probe(struct gps_device_t *session) */ static void garmin_init(struct gps_device_t *session) { - bool ret; - - gpsd_report(5, "to garmin_probe()\n"); - ret = garmin_probe( session ); - /* FIXME - what if return code was bad */ - /* FIXME - return code is always bad */ - gpsd_report(3, "from garmin_probe() = %d\n", (int)ret); + // Tell the device to send product data + gpsd_report(3, "Get Garmin Product Data\n"); + Build_Send_SER_Packet(session, GARMIN_LAYERID_APPL + , GARMIN_PKTID_PRODUCT_RQST, 0, 0); // turn on PVT data 49 gpsd_report(3, "Set Garmin to send reports every 1 second\n"); - Build_Send_Packet(session, GARMIN_LAYERID_APPL + Build_Send_SER_Packet(session, GARMIN_LAYERID_APPL , GARMIN_PKTID_L001_COMMAND_DATA, 2, CMND_START_PVT_DATA); +#if USE_RMD // turn on RMD data 110 - // Build_Send_Packet(session, GARMIN_LAYERID_APPL - // , GARMIN_PKTID_L001_COMMAND_DATA, 2, CMND_START_RM_DATA); + gpsd_report(3, "Set Garmin to send Raw sat data\n"); + Build_Send_SER_Packet(session, GARMIN_LAYERID_APPL + , GARMIN_PKTID_L001_COMMAND_DATA, 2, CMND_START_RM_DATA); +#endif } static void garmin_close(struct gps_device_t *session UNUSED) @@ -938,16 +816,8 @@ static void garmin_close(struct gps_device_t *session UNUSED) return; } -static ssize_t garmin_get_packet(struct gps_device_t *session) -{ - return (ssize_t)( 0 == GetPacket( session ) ? 1 : 0); -} - -static gps_mask_t garmin_usb_parse(struct gps_device_t *session) -{ - gpsd_report(5, "garmin_usb_parse()\n"); - return PrintUSBPacket(session, (Packet_t*)session->driver.garmin.Buffer); -} +#define Send_ACK() Build_Send_SER_Packet(session, 0, ACK, 0, 0) +#define Send_NAK() Build_Send_SER_Packet(session, 0, NAK, 0, 0) /*@ +charint @*/ gps_mask_t garmin_ser_parse(struct gps_device_t *session) @@ -969,6 +839,7 @@ gps_mask_t garmin_ser_parse(struct gps_device_t *session) if ( 6 > len ) { /* WTF? */ /* minimum packet; <DLE> [pkt id] [length=0] [chksum] <DLE> <STX> */ + Send_NAK(); gpsd_report(6, "Garmin serial too short: %#2x\n", len); return 0; } @@ -978,6 +849,7 @@ gps_mask_t garmin_ser_parse(struct gps_device_t *session) } if ( '\x10' != buf[0] ) { + Send_NAK(); gpsd_report(6, "buf[0] not DLE\n", buf[0]); return 0; } @@ -986,6 +858,7 @@ gps_mask_t garmin_ser_parse(struct gps_device_t *session) chksum = pkt_id; if ( '\x10' == pkt_id ) { if ( '\x10' != buf[n++] ) { + Send_NAK(); gpsd_report(6, "Bad pkt_id %#02x\n", pkt_id); return 0; } @@ -996,6 +869,7 @@ gps_mask_t garmin_ser_parse(struct gps_device_t *session) if ( '\x10' == pkt_len ) { if ( '\x10' != buf[n++] ) { gpsd_report(6, "Bad pkt_len %#02x\n", pkt_len); + Send_NAK(); return 0; } } @@ -1008,12 +882,14 @@ gps_mask_t garmin_ser_parse(struct gps_device_t *session) } if ( len < n + i ) { gpsd_report(6, "Packet too short %#02x < %#0x\n", len, n + i); + Send_NAK(); return 0; } c = buf[n + i]; if ( got_dle ) { got_dle = 0; if ( '\x10' != c ) { + Send_NAK(); gpsd_report(6, "Bad DLE %#02x\n", c); return 0; } @@ -1027,6 +903,7 @@ gps_mask_t garmin_ser_parse(struct gps_device_t *session) } /* get checksum */ if ( len < n + i ) { + Send_NAK(); gpsd_report(6, "No checksum, Packet too short %#02x < %#0x\n" , len, n + i); return 0; @@ -1035,23 +912,27 @@ gps_mask_t garmin_ser_parse(struct gps_device_t *session) chksum += c; /* get final DLE */ if ( len < n + i ) { + Send_NAK(); gpsd_report(6, "No final DLE, Packet too short %#02x < %#0x\n" , len, n + i); return 0; } c = buf[n + i++]; if ( '\x10' != c ) { + Send_NAK(); gpsd_report(6, "Final DLE not DLE\n", c); return 0; } /* get final ETX */ if ( len < n + i ) { + Send_NAK(); gpsd_report(6, "No final ETX, Packet too short %#02x < %#0x\n" , len, n + i); return 0; } c = buf[n + i++]; if ( '\x03' != c ) { + Send_NAK(); gpsd_report(6, "Final ETX not ETX\n", c); return 0; } @@ -1062,6 +943,8 @@ gps_mask_t garmin_ser_parse(struct gps_device_t *session) gpsd_report(6, "Char: %#02x\n", data_buf[i]); } + Send_ACK(); + gpsd_report(4 , "garmin_ser_parse() Type: %#02x, Len: %#02x, chksum: %#02x\n" , pkt_id, pkt_len, chksum); @@ -1073,7 +956,126 @@ gps_mask_t garmin_ser_parse(struct gps_device_t *session) /*@ -charint @*/ /* this is everything we export */ -struct gps_type_t garmin_usb_binary = +#ifdef __UNUSED__ +static int GetPacket (struct gps_device_t *session ); +//----------------------------------------------------------------------------- +// Gets a single packet. +// this is odd, the garmin usb driver will only return 64 bytes, or less +// at a time, no matter what you ask for. +// +// is you ask for less than 64 bytes then the next packet will include +// just the remaining bytes of the last 64 byte packet. +// +// Reading a packet of length Zero, or less than 64, signals the end of +// the entire packet. +// +// The Garmin sample WinXX code also assumes the same behavior, so +// maybe it is something in the USB protocol. +// +// Return: 0 = got a good packet +// -1 = error +// 1 = got partial packet +static int GetPacket (struct gps_device_t *session ) +{ + struct timespec delay, rem; + int cnt = 0; + // int x = 0; // for debug dump + + memset( session->driver.garmin.Buffer, 0, sizeof(Packet_t)); + memset( &delay, 0, sizeof(delay)); + session->driver.garmin.BufferLen = 0; + session->outbuflen = 0; + + gpsd_report(4, "GetPacket()\n"); + + for( cnt = 0 ; cnt < 10 ; cnt++ ) { + size_t pkt_size; + // Read async data until the driver returns less than the + // max async data size, which signifies the end of a packet + + // not optimal, but given the speed and packet nature of + // the USB not too bad for a start + ssize_t theBytesReturned = 0; + uint8_t *buf = (uint8_t *)session->driver.garmin.Buffer; + Packet_t *thePacket = (Packet_t*)buf; + + theBytesReturned = read(session->gpsdata.gps_fd + , buf + session->driver.garmin.BufferLen + , ASYNC_DATA_SIZE); + // zero byte returned is a legal value and denotes the end of a + // binary packet. + if ( 0 > theBytesReturned ) { + // read error... + // or EAGAIN, but O_NONBLOCK is never set + gpsd_report(0, "GetPacket() read error=%d, errno=%d\n" + , theBytesReturned, errno); + continue; + } + gpsd_report(5, "got %d bytes\n", theBytesReturned); +#if 1 + gpsd_report(4, "getPacket(), got %d bytes: %s\n" + , theBytesReturned, gpsd_hexdump(thePacket, theBytesReturned)); +#endif + + session->driver.garmin.BufferLen += theBytesReturned; + if ( 256 <= session->driver.garmin.BufferLen ) { + // really bad read error... + gpsd_report(3, "GetPacket() packet too long, %ld > 255 !\n" + , session->driver.garmin.BufferLen); + session->driver.garmin.BufferLen = 0; + break; + } + pkt_size = 12 + get_int32((uint8_t*)&thePacket->mDataSize); + if ( 12 <= session->driver.garmin.BufferLen) { + // have enough data to check packet size + if ( session->driver.garmin.BufferLen > pkt_size) { + // wrong amount of data in buffer + gpsd_report(3 + , "GetPacket() packet size wrong! Packet: %ld, s/b %ld\n" + , session->driver.garmin.BufferLen + , pkt_size); + session->driver.garmin.BufferLen = 0; + break; + } + } + if ( 64 > theBytesReturned ) { + // zero length, or short, read is a flag for got the whole packet + break; + } + + + /*@ ignore @*/ + delay.tv_sec = 0; + delay.tv_nsec = 3330000L; + while (nanosleep(&delay, &rem) < 0) + continue; + /*@ end @*/ + } + // dump the individual bytes, debug only + // for ( x = 0; x < session->driver.garmin.BufferLen; x++ ) { + // gpsd_report(6, "p[%d] = %x\n", x, session->driver.garmin.Buffer[x]); + // } + if ( 10 <= cnt ) { + gpsd_report(3, "GetPacket() packet too long or too slow!\n"); + return -1; + } + + gpsd_report(5, "GotPacket() sz=%d \n", session->driver.garmin.BufferLen); + session->outbuflen = session->driver.garmin.BufferLen; + return 0; +} +static gps_mask_t garmin_usb_parse(struct gps_device_t *session) +{ + gpsd_report(5, "garmin_usb_parse()\n"); + return PrintUSBPacket(session, (Packet_t*)session->driver.garmin.Buffer); +} + +static ssize_t garmin_get_packet(struct gps_device_t *session) +{ + return (ssize_t)( 0 == GetPacket( session ) ? 1 : 0); +} + +struct gps_type_t garmin_usb_binary_old = { .typename = "Garmin USB binary", /* full name of type */ .trigger = NULL, /* no trigger, it has a probe */ @@ -1091,6 +1093,26 @@ struct gps_type_t garmin_usb_binary = .wrapup = garmin_close, /* close hook */ .cycle = 1, /* updates every second */ }; +#endif /* __UNUSED__ */ + +struct gps_type_t garmin_usb_binary = +{ + .typename = "Garmin USB binary", /* full name of type */ + .trigger = NULL, /* no trigger, it has a probe */ + .channels = GARMIN_CHANNELS, /* consumer-grade GPS */ + .wakeup = NULL, /* no wakeup to be done before hunt */ + .probe = garmin_probe, /* how to detect at startup time */ + .initializer = garmin_init, /* initialize the device */ + .get_packet = packet_get, /* how to grab a packet */ + .parse_packet = garmin_ser_parse, /* parse message packets */ + .rtcm_writer = NULL, /* don't send DGPS corrections */ + .speed_switcher = NULL, /* no speed switcher */ + .mode_switcher = NULL, /* no mode switcher */ + .rate_switcher = NULL, /* no sample-rate switcher */ + .cycle_chars = -1, /* not relevant, no rate switch */ + .wrapup = garmin_close, /* close hook */ + .cycle = 1, /* updates every second */ +}; struct gps_type_t garmin_ser_binary = { |