void gusb_id_unit(struct garmin_unit_info *gu) { static const char oid[12] = {20, 0, 0, 0, 0xfe, 0, 0, 0, 0, 0, 0, 0}; garmin_usb_packet iresp; int i; gusb_cmd_send((garmin_usb_packet *)oid, sizeof(oid)); for (i = 0; i < 25; i++) { iresp.gusb_pkt.type = 0; if (gusb_cmd_get(&iresp, sizeof(iresp)) < 0) { return; } if (le_read16(iresp.gusb_pkt.pkt_id) == 0xff) { gu->product_identifier = xstrdup((char *) iresp.gusb_pkt.databuf+4); gu->unit_id = le_read16(iresp.gusb_pkt.databuf+0); gu->unit_version = le_read16(iresp.gusb_pkt.databuf+2); } /* * My goodnesss, this is fragile. During command syncup, * we need to know if we're at the end. The 0xfd packet * is promised by Garmin engineering to be the last. */ if (le_read16(iresp.gusb_pkt.pkt_id) == 0xfd) return; } fatal("Unable to sync with Garmin USB device in %d attempts.", i); }
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"); }
int32 GPS_Write_Packet_usb(gpsdevh *dh, GPS_PPacket packet) { garmin_usb_packet gp; memset(&gp, 0, sizeof(gp)); /* * Take the "portable" GPS_Packet data and put them into * the USB packet that we will put on the wire. */ gp.gusb_pkt.type = 0x14; le_write16(&gp.gusb_pkt.pkt_id, packet->type); le_write32(&gp.gusb_pkt.datasz, packet->n ); memcpy(&gp.gusb_pkt.databuf, packet->data, packet->n); return gusb_cmd_send(&gp, packet->n + 12); }
int gusb_cmd_send(const garmin_usb_packet *opkt, size_t sz) { unsigned int rv, i; unsigned char *obuf = (unsigned char *) &opkt->dbuf; const char *m1, *m2; rv = gusb_llops->llop_send(opkt, sz); if (gps_show_bytes) { const unsigned short pkttype = le_read16(&opkt->gusb_pkt.databuf[0]); const unsigned short pkt_id = le_read16(&opkt->gusb_pkt.pkt_id); GPS_Diag("TX [%d]:", sz); for(i=0;i<sz;i++) GPS_Diag("%02x ", obuf[i]); for(i=0;i<sz;i++) GPS_Diag("%c", isalnum(obuf[i])? obuf[i] : '.'); m1 = Get_Pkt_Type(pkt_id, pkttype, &m2); GPS_Diag("(%-8s%s)\n", m1, m2 ? m2 : ""); } /* * Recursion, when used in a disciplined way, can be our friend. * * The Garmin protocol requires that packets that are exactly * a multiple of the max tx size be followed by a zero length * packet. Do that here so we can see it in debugging traces. */ if (sz && !(sz % gusb_llops->max_tx_size)) { gusb_cmd_send(opkt, 0); } 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; }