void gusb_syncup(void) { static int unit_number; static const char oinit[12] = {0, 0, 0, 0, GUSB_SESSION_START, 0, 0, 0, 0, 0, 0, 0}; garmin_usb_packet iresp; int i; /* * This is our first communication with the unit. */ receive_state = rs_fromintr; for(i = 0; i < 25; i++) { le_write16(&iresp.gusb_pkt.pkt_id, 0); le_write32(&iresp.gusb_pkt.datasz, 0); le_write32(&iresp.gusb_pkt.databuf, 0); gusb_cmd_send((const garmin_usb_packet *) oinit, sizeof(oinit)); gusb_cmd_get(&iresp, sizeof(iresp)); if ((le_read16(iresp.gusb_pkt.pkt_id) == GUSB_SESSION_ACK) && (le_read32(iresp.gusb_pkt.datasz) == 4)) { unsigned serial_number = le_read32(iresp.gusb_pkt.databuf); garmin_unit_info[unit_number].serial_number = serial_number; gusb_id_unit(&garmin_unit_info[unit_number]); unit_number++; return; } } fatal("Unable to establish USB syncup\n"); }
/* * Return values are: * Negative on error. * 1 if read success - even if empty packet. */ int32 GPS_Packet_Read_usb(gpsdevh *dh, GPS_PPacket *packet, int eat_bulk) { int32 n; int32 payload_size; garmin_usb_packet pkt; memset(&pkt, 0, sizeof(pkt)); do_over: n = gusb_cmd_get(&pkt, sizeof(pkt)); if ( n < 0 ) { /* * We (probably) used to have a GPS and it went away * while we were speaking with it. Perhaps batteries * died or it was unplugged or something. */ gps_errno = PROTOCOL_ERROR; return n; } /* * This is a horrible hack for 276/296. This family sometimes * switches between bulk and interrupt on EVERY packet. Rather * than bother all the callers with that bit of unpleasantness, * silently consume zero byte "switch back to intr" packets here. * * The one caller that doesn't want this hidden is device discovery * in the A000 handler. */ if ((n == 0) && eat_bulk) { goto do_over; } /* * Populate members of serial packet from USB packet. The * copy here seems wasteful, but teaching all the callers about * a structure with the "data" member being in a different place * (Since the protocol packets was badly exposed in the core * design of jeeps) is even more painful. */ (*packet)->type = le_read16(&pkt.gusb_pkt.pkt_id); payload_size = le_read32(&pkt.gusb_pkt.datasz); if (payload_size<0 || payload_size>MAX_GPS_PACKET_SIZE) { GPS_Error("GPS_Packet_Read_usb: Bad payload size %d", payload_size); gps_errno = FRAMING_ERROR; return 0; } (*packet)->n = payload_size; memcpy((*packet)->data, &pkt.gusb_pkt.databuf, payload_size); return 1; }
static void mmo_end_of_route(mmo_data_t *data) { #ifdef MMO_DBG const char *sobj = "CObjRoute"; #endif route_head *rte = data->data; int rtept = rte->rte_waypt_ct; int i; char buf[64]; if (data->visible && data->loop) { DBG((sobj, "route \"%s\" is a loop.\n", data->name)); (void) mmo_read_object(NULL); rtept--; } if (mmo_version >= 0x12) { mmo_fillbuf(buf, 7, 1); DBG((sobj, "route data (since 0x12): ")); mmo_printbuf(buf, 7, ""); rte->line_color.bbggrr = le_read32(&buf[0]); rte->line_color.opacity = 255 - (buf[6] * 51); DBG((sobj, "color = 0x%06X\n", rte->line_color.bbggrr)); DBG((sobj, "transparency = %d (-> %d)\n", buf[6], rte->line_color.opacity)); } if (data->visible) { for (i = 0; i < rtept; i++) (void) mmo_read_object(NULL); } if (data->loop && (data->done > 1)) { queue *elem; elem = QUEUE_FIRST(&rte->waypoint_list); dequeue(elem); ENQUEUE_TAIL(&rte->waypoint_list, elem); } if (rte->rte_waypt_ct == 0) { /* don't keep empty routes */ route_del_head(rte); data->data = NULL; } }
/* * This was a function of great joy to discover...and even greater to maintain. * * It turns out that as of 5/2006, every Garmin USB product has a problem * where the device does not reset the data toggles after a configuration * set. After a reset, the toggles both match. So we tear through the * conversation and life is good. Unfortunately, the second time through, * if we had an odd number of transactions in the previous conversation, * we send a configuration set and reset the toggle on the HCI but the * toggle on the device's end of the pipe is now out of whack which means * that the subsequent transaction will hang. * * This isn't a problem in Windows since the configuration set is done only * once there. * * This code has been tested in loops of 1000 cycles on Linux and OS/X and * it seems to cure this at a mere cost of complexity and startup time. I'll * be delighted when all the firmware gets revved and updated and we can * remove this. * * 9/2008 But wait, there's more. The very toggle reset that we *had* to * implement in 2006 to make non-Windows OSes work actually locks up verion * 2.70 of the Venture HC. On that model, the second product request * (you know, the one that we *use*, locks that device's protocol stack * after the RET2INTR that immediately follows the REQBLK (and why is it * telling us to go into bulk mode followed by an immeidate EOF, anyway?) * that follows the request for product ID. 100% reproducible on Mac and * Linux. Of course, we don't see this on the Windows system becuase * we don't have to jump through hooops to clear the spec-violating out * of state toggles there becuase those systems see only one configuration * set ever. * * Grrrr! */ unsigned gusb_reset_toggles(void) { static const char oinit[12] = {0, 0, 0, 0, GUSB_SESSION_START, 0, 0, 0, 0, 0, 0, 0}; static const char oid[12] = {20, 0, 0, 0, 0xfe, 0, 0, 0, 0, 0, 0, 0}; garmin_usb_packet iresp; int t; unsigned rv = 0; /* Start off with three session starts. * #1 resets the bulk out toggle. It may not make it to the device. * #2 resets the the intr in toggle. It will make it to the device * since #1 reset the the bulk out toggle. The device will * respond on the intr in pipe which will clear its toggle. * #3 actually starts the session now that the above are both clear. */ gusb_cmd_send((const garmin_usb_packet *) oinit, sizeof(oinit)); gusb_cmd_send((const garmin_usb_packet *) oinit, sizeof(oinit)); gusb_cmd_send((const garmin_usb_packet *) oinit, sizeof(oinit)); t = 10; while(1) { le_write16(&iresp.gusb_pkt.pkt_id, 0); le_write32(&iresp.gusb_pkt.datasz, 0); le_write32(&iresp.gusb_pkt.databuf, 0); gusb_cmd_get(&iresp, sizeof(iresp)); if ((le_read16(iresp.gusb_pkt.pkt_id) == GUSB_SESSION_ACK) && (le_read32(iresp.gusb_pkt.datasz) == 4)) { break; } if (t-- <= 0) { fatal("Could not start session in a reasonable number of tries.\n"); } } /* * Now that the bulk out and intr in packets are good, we send * a product ID. On devices that respond totally on the intr * pipe, this does nothing interesting, but on devices that respon * on the bulk pipe this will reset the toggles on the bulk in. */ t = 10; gusb_cmd_send((const garmin_usb_packet *) oid, sizeof(oid)); while(1) { le_write16(&iresp.gusb_pkt.pkt_id, 0); le_write32(&iresp.gusb_pkt.datasz, 0); le_write32(&iresp.gusb_pkt.databuf, 0); gusb_cmd_get(&iresp, sizeof(iresp)); if (le_read16(iresp.gusb_pkt.pkt_id) == 0xff) { rv = le_read16(iresp.gusb_pkt.databuf+0); } if (le_read16(iresp.gusb_pkt.pkt_id) == 0xfd) return rv; if (t-- <= 0) { fatal("Could not start session in a reasonable number of tries.\n"); } } return 0; }
static void mmo_read_CObjWaypoint(mmo_data_t *data) { #ifdef MMO_DBG const char *sobj = "CObjWaypoint"; #endif waypoint *wpt; time_t time; int rtelinks; mmo_data_t **rtelink = NULL; char *str; char buf[16]; int i, ux; DBG((sobj, ":-----------------------------------------------------\n")); DBG((sobj, "name = \"%s\" [ visible=%s, id=0x%04X ]\n", data->name, data->visible ? "yes" : "NO", data->objid)); wpt = waypt_new(); wpt->shortname = xstrdup(data->name); time = data->mtime; if (! time) time = data->ctime; if (time > 0) wpt->creation_time = time; wpt->latitude = gbfgetdbl(fin); wpt->longitude = gbfgetdbl(fin); DBG((sobj, "coordinates = %f / %f\n", wpt->latitude, wpt->longitude)); rtelinks = gbfgetuint16(fin); if (rtelinks > 0) { rtelink = xcalloc(sizeof(*rtelink), rtelinks); DBG((sobj, "rtelinks = %d\n", rtelinks)); for (i = 0; i < rtelinks; i++) { mmo_data_t *tmp; int objid; DBG((sobj, "read rtelink number %d\n", i + 1)); objid = gbfgetuint16(fin); gbfseek(fin, -2, SEEK_CUR); rtelink[i] = tmp = mmo_read_object(wpt); if ((objid < 0x8000) && (tmp != NULL) && (tmp->type == rtedata)) { route_head *rte = tmp->data; tmp->left--; route_add_wpt(rte, waypt_dupe(wpt)); DBG((sobj, "\"%s\" Added to route \"%s\"\n", wpt->shortname, rte->rte_name)); } } } str = mmo_readstr(); /* descr + url */ if (strncmp(str, "_FILE_ ", 7) == 0) { char *cx, *cend; cx = lrtrim(str + 7); cend = strchr(cx, '\n'); if (cend == NULL) cend = cx + strlen(cx); cx = lrtrim(xstrndup(cx, cend - cx)); if (*cx) wpt->url = cx; else xfree(cx); if (*cend++) wpt->notes = xstrdup(cend); if (wpt->url) DBG((sobj, "url = \"%s\"\n", wpt->url)); } else if (*str) wpt->notes = xstrdup(str); xfree(str); if (wpt->notes) DBG((sobj, "notes = \"%s\"\n", wpt->notes)); mmo_fillbuf(buf, 12, 1); i = le_read32(&buf[8]); /* icon */ if (i != -1) { char key[16]; char *name; snprintf(key, sizeof(key), "%d", i); if (avltree_find(icons, key, (void *)&name)) { wpt->icon_descr = xstrdup(name); wpt->wpt_flags.icon_descr_is_dynamic = 1; DBG((sobj, "icon = \"%s\"\n", wpt->icon_descr)); } } wpt->proximity = le_read_float(&buf[4]); if (wpt->proximity) { wpt->wpt_flags.proximity = 1; DBG((sobj, "proximity = %f\n", wpt->proximity)); } str = mmo_readstr(); /* name on gps ??? option ??? */ if (*str) { wpt->description = wpt->shortname; wpt->shortname = str; DBG((sobj, "name on gps = %s\n", str)); } else xfree(str); ux = gbfgetuint32(fin); DBG((sobj, "proximity type = %d\n", ux)); if (rtelinks) { int i; for (i = 0; i < rtelinks; i++) { int j; route_head *rte = rtelink[i]->data; for (j = 0; j < rtelinks; j++) { if ((i != j) && (rtelink[i] == rtelink[j])) { rtelink[i]->loop = 1; break; } } rtelink[i]->done++; if ((rtelink[i]->left == 0) && (rtelink[i]->done == rte->rte_waypt_ct)) { if (mmo_version <= 0x11) mmo_end_of_route(rtelink[i]); } } } if (rtelink) { xfree(rtelink); waypt_free(wpt); data->data = NULL; } else waypt_add(wpt); }
/* * Return values are: * Negative on error. * 1 if read success - even if empty packet. */ int32 GPS_Packet_Read_usb(gpsdevh *dh, GPS_PPacket *packet, int eat_bulk) { int32 n; int32 payload_size; garmin_usb_packet pkt; memset(&pkt, 0, sizeof(pkt)); do_over: n = gusb_cmd_get(&pkt, sizeof(pkt)); if ( n < 0 ) { /* * We (probably) used to have a GPS and it went away * while we were speaking with it. Perhaps batteries * died or it was unplugged or something. */ gps_errno = PROTOCOL_ERROR; return n; } /* * This is a horrible hack for 276/296. This family sometimes * switches between bulk and interrupt on EVERY packet. Rather * than bother all the callers with that bit of unpleasantness, * silently consume zero byte "switch back to intr" packets here. * * The one caller that doesn't want this hidden is device discovery * in the A000 handler. */ if ((n == 0) && eat_bulk) { goto do_over; } /* We sometimes get corrupted packets during a track log transfer * where the first byte in a packet is lost, causing all remaining * bytes in this packet to be shifted. So far, this has only been * observed on a Forerunner 305 (both on Linux and Windows). The * cause is unknown, but it seems to be timing dependent. * We try to detect the corruption mainly by checking reserved bytes * 3 and 7 which normally should be 0, the remaining comparisons are * only sanity checks and they alone could also trigger in case of * valid packets. Note: We can't detect corrupted packets with an ID * or length that's a multiple of 256, but such corrupted packets * haven't been observed so far. */ if (gps_save_id == 484 && pkt.gusb_pkt.type == 0 && pkt.gusb_pkt.reserved1 == 0 && pkt.gusb_pkt.reserved2 == 0 && pkt.gusb_pkt.reserved3 != 0 && pkt.gusb_pkt.pkt_id[0] <= 4 && pkt.gusb_pkt.pkt_id[1] == 0 && pkt.gusb_pkt.reserved6 == 0 && pkt.gusb_pkt.reserved7 != 0) { memmove(&pkt.dbuf[1], &pkt.dbuf[0], sizeof(pkt) - 1); pkt.gusb_pkt.type = 20; } /* * Populate members of serial packet from USB packet. The * copy here seems wasteful, but teaching all the callers about * a structure with the "data" member being in a different place * (Since the protocol packets was badly exposed in the core * design of jeeps) is even more painful. */ (*packet)->type = le_read16(&pkt.gusb_pkt.pkt_id); payload_size = le_read32(&pkt.gusb_pkt.datasz); if (payload_size<0 || payload_size>MAX_GPS_PACKET_SIZE) { /* If you get this, the packet might have been corrupted * by the unit. Have a look at the corruption detection * code above. */ GPS_Error("GPS_Packet_Read_usb: Bad payload size %d", payload_size); gps_errno = FRAMING_ERROR; return 0; } (*packet)->n = payload_size; memcpy((*packet)->data, &pkt.gusb_pkt.databuf, payload_size); return 1; }
static void lowranceusr_parse_waypt(waypoint *wpt_tmp) { char buff[MAXUSRSTRINGSIZE + 1]; int text_len; time_t waypt_time; short waypt_type; wpt_tmp->latitude = lat_mm_to_deg(gbfgetint32(file_in)); wpt_tmp->longitude = lon_mm_to_deg(gbfgetint32(file_in)); wpt_tmp->altitude = FEET_TO_METERS(gbfgetint32(file_in)); if (wpt_tmp->altitude <= UNKNOWN_USR_ALTITUDE) { wpt_tmp->altitude = unknown_alt; } text_len = lowranceusr_readstr(&buff[0], MAXUSRSTRINGSIZE, file_in); if (text_len) { buff[text_len] = '\0'; wpt_tmp->shortname = xstrdup(buff); } if (global_opts.debug_level >= 1) printf(MYNAME " parse_waypt: Waypt name = %s Lat = %f Lon = %f alt = %f\n",wpt_tmp->shortname, wpt_tmp->latitude, wpt_tmp->longitude, wpt_tmp->altitude); text_len = lowranceusr_readstr(&buff[0], MAXUSRSTRINGSIZE, file_in); if (text_len) { buff[text_len] = '\0'; wpt_tmp->description = xstrdup(buff); } /* Time is number of seconds since Jan. 1, 2000 */ waypt_time = gbfgetint32(file_in); if (waypt_time) wpt_tmp->creation_time = base_time_secs + waypt_time; if (global_opts.debug_level >= 2) { printf(MYNAME " parse_waypt: creation time %d\n", (int)wpt_tmp->creation_time); printf(MYNAME " parse_waypt: base_time %d\n", (int)base_time_secs); printf(MYNAME " parse_waypt: waypt time %d\n", (int)waypt_time); } /* Symbol ID */ wpt_tmp->icon_descr = lowranceusr_find_desc_from_icon_number(gbfgetint32(file_in)); if (!wpt_tmp->icon_descr[0]) { char nbuf[10]; snprintf(nbuf, sizeof(nbuf), "%d", le_read32(buff)); wpt_tmp->wpt_flags.icon_descr_is_dynamic = 1; wpt_tmp->icon_descr = xstrdup(nbuf); } /* Waypoint Type (USER, TEMPORARY, POINT_OF_INTEREST) */ waypt_type = gbfgetint16(file_in); if (global_opts.debug_level >= 1) printf(MYNAME " parse_waypt: waypt_type = %d\n",waypt_type); // Version 3 has a depth field here. if (reading_version >= 3) { float depth_feet = gbfgetflt(file_in); if (abs(depth_feet - 99999.0) > .1) WAYPT_SET(wpt_tmp, depth, FEET_TO_METERS(depth_feet)); } }