static void psp_read(void) { char buff[MAXPSPSTRINGSIZE + 1]; double radians; double lat, lon; waypoint *wpt_tmp; short int pincount; short int pindex; char gridbyte = 0x00; char *tmp; /* 32 bytes - file header */ psp_fread(&buff[0], 1, 32, psp_file_in); if (valid_psp_header(buff) != 0) { fatal(MYNAME ": input file does not appear to be a valid .PSP file.\n"); } pincount = le_read16(&buff[12]); while (pincount--) { wpt_tmp = waypt_new(); wpt_tmp->altitude = unknown_alt; /* offset 0x20 - 0x21 pin index */ psp_fread(&pindex, 1, 2, psp_file_in); /* offset 0x22 - 0x23 */ psp_fread(&buff[0], 1, 2, psp_file_in); /* offset 0x24 */ /* 1 byte, the grid byte - needed for sign corrections later*/ psp_fread(&gridbyte, 1, 1, psp_file_in); /* 8 bytes - latitude in radians */ radians = psp_fread_double(psp_file_in); lat = DEG(radians); /* 8 bytes - longitude in radians */ radians = psp_fread_double(psp_file_in); lon = DEG(radians); /* since we don't know the origin of this PSP file, we use */ /* the grid byte adjust longitude, if necessary, mimicing */ /* the behavior of pocketstreets correcting the data. This */ /* does not correct the fact that points in eastern US are */ /* written with the wrong coordinates by S&T. (MS bug) */ decode_psp_coordinates(&lat, &lon, gridbyte); wpt_tmp->latitude = lat; wpt_tmp->longitude = lon; /* 1 byte - pin display properties */ psp_fread(&buff[0], 1, 1, psp_file_in); /* 3 bytes - unknown */ psp_fread(&buff[0], 1, 3, psp_file_in); /* 1 bytes - icon (values: 0x00 - 0x27) */ psp_fread(&buff[0], 1, 1, psp_file_in); /* 3 bytes - unknown */ psp_fread(&buff[0], 1, 3, psp_file_in); wpt_tmp->shortname = psp_read_str(psp_file_in); wpt_tmp->description = psp_read_str(psp_file_in); tmp = psp_read_str(psp_file_in); /* (address?) */ if (tmp) xfree(tmp); waypt_add(wpt_tmp); } }
int gusb_cmd_get(garmin_usb_packet *ibuf, size_t sz) { int rv = 0; unsigned char *buf = (unsigned char *) &ibuf->dbuf; int orig_receive_state; unsigned short pkt_id; top: orig_receive_state = receive_state; switch (receive_state) { case rs_fromintr: rv = gusb_llops->llop_get_intr(ibuf, sz); break; case rs_frombulk: rv = gusb_llops->llop_get_bulk(ibuf, sz); break; default: fatal("Unknown receiver state %d\n", receive_state); } pkt_id = le_read16(&ibuf->gusb_pkt.pkt_id); if (gps_show_bytes) { int i; const char *m1, *m2; unsigned short pkttype = le_read16(&ibuf->gusb_pkt.databuf[0]); GPS_Diag("RX (%s) [%d]:", receive_state == rs_fromintr ? "intr" : "bulk", rv); for(i=0;i<rv;i++) { /*dsr if (DEBUG_THRESH) { GPS_Diag("[...]"); break; } */ GPS_Diag("%02x ", buf[i]); } for(i=0;i<rv;i++) { /*dsr if (DEBUG_THRESH) { GPS_Diag("[...]"); break; } */ GPS_Diag("%c", isalnum(buf[i])? buf[i] : '.'); } m1 = Get_Pkt_Type(pkt_id, pkttype, &m2); if ((rv == 0) && (receive_state == rs_frombulk) ) {m1= "RET2INTR";m2=NULL;}; GPS_Diag("(%-8s%s)\n", m1, m2 ? m2 : ""); } /* Adjust internal state and retry the read */ if ((rv > 0) && (pkt_id == GUSB_REQUEST_BULK)) { receive_state = rs_frombulk; goto top; } /* * If we were reading from the bulk pipe and we just got * a zero request, adjust our internal state. * It's tempting to retry the read here to hide this "stray" * packet from our callers, but that only works when you know * there's another packet coming. That works in every case * except the A000 discovery sequence. */ if ((receive_state == rs_frombulk) && (rv <= 0)) { receive_state = rs_fromintr; } return rv; }
/* * 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; }
/* * 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; }