OpenCPN Partial API docs
Loading...
Searching...
No Matches
garmin_protocol_mgr.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: Garmin NMEA Data Stream Object
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2010 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 ***************************************************************************
25
26 ***************************************************************************
27 * Parts of this file were adapted from source code found in *
28 * John F. Waers (jfwaers@csn.net) public domain program MacGPS45 *
29 ***************************************************************************
30 *
31 */
32
33#include <stdlib.h>
34#include <math.h>
35#include <time.h>
36
37#include <vector>
38
39#ifndef _MSC_VER
40#include <arpa/inet.h>
41#include <netinet/tcp.h>
42#endif
43
44#include <wx/wxprec.h>
45
46#ifndef WX_PRECOMP
47#include <wx/wx.h>
48#endif // precompiled headers
49
50#include <wx/datetime.h>
51#include <wx/event.h>
52#include <wx/log.h>
53#include <wx/string.h>
54#include <wx/tokenzr.h>
55#include <wx/utils.h>
56
57#ifdef __WXMSW__
58#include <windows.h>
59#include <winioctl.h>
60#include <initguid.h>
61#include <setupapi.h>
62#endif
63
64#include "dychart.h"
65#include "garmin_wrapper.h"
66#include "garmin_protocol_mgr.h"
67#include "nmea0183.h"
68
69#ifdef __ANDROID__
70#include "androidUTIL.h"
71#endif
72
73
74#if !defined(NAN)
75static const long long lNaN = 0xfff8000000000000;
76#define NAN (*(double *)&lNaN)
77#endif
78
79extern bool g_benableUDPNullHeader;
80
81#define N_DOG_TIMEOUT 5
82
83#ifdef __WXMSW__
84// {2C9C45C2-8E7D-4C08-A12D-816BBAE722C0}
85DEFINE_GUID(GARMIN_GUID1, 0x2c9c45c2L, 0x8e7d, 0x4c08, 0xa1, 0x2d, 0x81, 0x6b,
86 0xba, 0xe7, 0x22, 0xc0);
87#endif
88
89//----------------------------------------------------------------------------
90// Garmin Device Management
91// Handle USB and Serial Port Garmin PVT protocol data interface.
92//----------------------------------------------------------------------------
93
94#ifdef __WXMSW__
95BOOL IsUserAdmin(VOID)
96/*++
97 * Routine Description: This routine returns TRUE if the caller's
98 * process is a member of the Administrators local group. Caller is
99 * NOT expected to be impersonating anyone and is expected to be able to open
100 * its own process and process token. Arguments: None. Return Value: TRUE -
101 * Caller has Administrators local group. FALSE - Caller does not have
102 * Administrators local group. --
103 */
104{
105 BOOL b;
106 SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
107 PSID AdministratorsGroup;
108 b = AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
109 DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
110 &AdministratorsGroup);
111 if (b) {
112 if (!CheckTokenMembership(NULL, AdministratorsGroup, &b)) {
113 b = FALSE;
114 }
115 FreeSid(AdministratorsGroup);
116 }
117
118 return (b);
119}
120
121void le_write16(void *addr, const unsigned value) {
122 unsigned char *p = (unsigned char *)addr;
123 p[0] = value;
124 p[1] = value >> 8;
125}
126
127void le_write32(void *addr, const unsigned value) {
128 unsigned char *p = (unsigned char *)addr;
129 p[0] = value;
130 p[1] = value >> 8;
131 p[2] = value >> 16;
132 p[3] = value >> 24;
133}
134
135signed int le_read16(const void *addr) {
136 const unsigned char *p = (const unsigned char *)addr;
137 return p[0] | (p[1] << 8);
138}
139
140signed int le_read32(const void *addr) {
141 const unsigned char *p = (const unsigned char *)addr;
142 return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
143}
144
145#endif
146
147BEGIN_EVENT_TABLE(GarminProtocolHandler, wxEvtHandler)
148EVT_TIMER(TIMER_GARMIN1, GarminProtocolHandler::OnTimerGarmin1)
149END_EVENT_TABLE()
150
152 wxEvtHandler *MessageTarget,
153 bool bsel_usb) {
154 m_pparent = parent;
155 m_pMainEventHandler = MessageTarget;
156 m_garmin_serial_thread = NULL;
157 m_garmin_usb_thread = NULL;
158 m_bOK = false;
159 m_busb = bsel_usb;
160
161 // Connect(wxEVT_OCPN_GARMIN,
162 // (wxObjectEventFunction)(wxEventFunction)&GarminProtocolHandler::OnEvtGarmin);
163
164 char pvt_on[14] = {20, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 49, 0};
165
166 char pvt_off[14] = {20, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 50, 0};
167
168#ifdef __WXMSW__
169 if (m_busb) {
170 m_usb_handle = INVALID_HANDLE_VALUE;
171
172 m_bneed_int_reset = true;
173 m_receive_state = rs_fromintr;
174 m_ndelay = 0;
175
176 wxLogMessage(_T("Searching for Garmin DeviceInterface and Device..."));
177
178 if (!FindGarminDeviceInterface()) {
179 wxLogMessage(_T(" Find:Is the Garmin USB driver installed?"));
180 } else {
181 if (!ResetGarminUSBDriver())
182 wxLogMessage(_T(" Reset:Is the Garmin USB Device plugged in?"));
183 }
184 }
185#endif
186
187 // Not using USB, so try a Garmin port open and device ident
188 if (!m_busb) {
189
190 //FIXME (dave)
191 //m_port = m_pparent->GetPort().AfterFirst(':'); // strip "Serial:"
192
193 // Start handler thread
194
195 // FIXME (dave)
196 //m_garmin_serial_thread =
197 // new GARMIN_Serial_Thread(this, m_pparent, m_pMainEventHandler, m_port);
198 //m_Thread_run_flag = 1;
199 //m_garmin_serial_thread->Run();
200 }
201
202 TimerGarmin1.SetOwner(this, TIMER_GARMIN1);
203 TimerGarmin1.Start(100);
204}
205
206GarminProtocolHandler::~GarminProtocolHandler() {}
207
208void GarminProtocolHandler::Close(void) {
209 TimerGarmin1.Stop();
210
211 StopIOThread(true);
212 StopSerialThread();
213}
214
215void GarminProtocolHandler::StopSerialThread(void) {
216 if (m_garmin_serial_thread) {
217 wxLogMessage(_T("Stopping Garmin Serial thread"));
218 m_Thread_run_flag = 0;
219
220 int tsec = 5;
221 while ((m_Thread_run_flag >= 0) && (tsec--)) {
222 wxSleep(1);
223 }
224
225 wxString msg;
226 if (m_Thread_run_flag < 0)
227 msg.Printf(_T("Stopped in %d sec."), 5 - tsec);
228 else
229 msg.Printf(_T("Not Stopped after 5 sec."));
230 wxLogMessage(msg);
231 }
232
233 m_garmin_serial_thread = NULL;
234}
235
236void GarminProtocolHandler::StopIOThread(bool b_pause) {
237 if (b_pause) TimerGarmin1.Stop();
238
239 if (m_garmin_usb_thread) {
240 wxLogMessage(_T("Stopping Garmin USB thread"));
241 m_Thread_run_flag = 0;
242
243 int tsec = 5;
244 while ((m_Thread_run_flag >= 0) && (tsec--)) {
245 wxSleep(1);
246 }
247
248 wxString msg;
249 if (m_Thread_run_flag < 0)
250 msg.Printf(_T("Stopped in %d sec."), 5 - tsec);
251 else
252 msg.Printf(_T("Not Stopped after 5 sec."));
253 wxLogMessage(msg);
254 }
255
256 m_garmin_usb_thread = NULL;
257
258#ifdef __WXMSW__
259 if (m_busb && (m_usb_handle != INVALID_HANDLE_VALUE))
260 CloseHandle(m_usb_handle);
261 m_usb_handle = INVALID_HANDLE_VALUE;
262#endif
263
264 m_ndelay = 30; // Fix delay for next restart
265}
266
267void GarminProtocolHandler::RestartIOThread(void) {
268 wxLogMessage(_T("Restarting Garmin I/O thread"));
269 TimerGarmin1.Start(1000);
270}
271
272void GarminProtocolHandler::OnTimerGarmin1(wxTimerEvent &event) {
273 char pvt_on[14] = {20, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 49, 0};
274
275 TimerGarmin1.Stop();
276
277 if (m_busb) {
278#ifdef __WXMSW__
279 // Try to open the Garmin USB device
280 if (INVALID_HANDLE_VALUE == m_usb_handle) {
281 if (INVALID_HANDLE_VALUE != garmin_usb_start()) {
282 // Send out a request for Garmin PVT data
283 m_receive_state = rs_fromintr;
284 gusb_cmd_send((const garmin_usb_packet *)pvt_on, sizeof(pvt_on));
285
286 // Start the pump
287 // FIXME (dave)
288 //m_garmin_usb_thread =
289 // new GARMIN_USB_Thread(this, m_pparent, m_pMainEventHandler,
290 // (wxIntPtr)m_usb_handle, m_max_tx_size);
291 m_Thread_run_flag = 1;
292 m_garmin_usb_thread->Run();
293 }
294 }
295#endif
296 }
297
298 TimerGarmin1.Start(1000);
299}
300
301#ifdef __WXMSW__
302bool GarminProtocolHandler::ResetGarminUSBDriver() {
303 OSVERSIONINFO version_info;
304 version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
305
306 if (GetVersionEx(&version_info)) {
307 if (version_info.dwMajorVersion > 5) {
308 if (!IsUserAdmin()) {
309 wxLogMessage(
310 _T(" GarminUSBDriver Reset skipped, requires elevated ")
311 _T("privileges on Vista and later...."));
312 return true;
313 }
314 }
315 }
316
317 HDEVINFO devs;
318 SP_DEVINFO_DATA devInfo;
319 SP_PROPCHANGE_PARAMS pchange;
320
321 devs = SetupDiGetClassDevs((GUID *)&GARMIN_GUID1, NULL, NULL,
322 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
323 if (devs == INVALID_HANDLE_VALUE) return false;
324
325 devInfo.cbSize = sizeof(devInfo);
326 if (!SetupDiEnumDeviceInfo(devs, 0, &devInfo)) {
327 wxLogMessage(_T(" GarminUSBDriver Reset0 failed..."));
328 return false;
329 }
330
331 pchange.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
332 pchange.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
333 pchange.StateChange = DICS_PROPCHANGE;
334 pchange.Scope = DICS_FLAG_CONFIGSPECIFIC;
335 pchange.HwProfile = 0;
336
337 if (!SetupDiSetClassInstallParams(devs, &devInfo, &pchange.ClassInstallHeader,
338 sizeof(pchange))) {
339 wxLogMessage(_T(" GarminUSBDriver Reset1 failed..."));
340 return false;
341 }
342
343 if (!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, devs, &devInfo)) {
344 wxLogMessage(_T(" GarminUSBDriver Reset2 failed..."));
345 return false;
346 }
347
348 wxLogMessage(_T("GarminUSBDriver Reset succeeded."));
349
350 return true;
351}
352
353bool GarminProtocolHandler::
354 FindGarminDeviceInterface() { // Search for a useable Garmin Device
355 // Interface Class
356
357 HDEVINFO hdevinfo;
358 SP_DEVINFO_DATA devInfo;
359
360 hdevinfo = SetupDiGetClassDevs((GUID *)&GARMIN_GUID1, NULL, NULL,
361 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
362
363 if (hdevinfo != INVALID_HANDLE_VALUE) {
364 devInfo.cbSize = sizeof(devInfo);
365 if (!SetupDiEnumDeviceInfo(hdevinfo, 0, &devInfo)) {
366 return false;
367 }
368 }
369
370 return true;
371}
372
373bool GarminProtocolHandler::IsGarminPlugged() {
374 DWORD size = 0;
375
376 HDEVINFO hdevinfo;
377 SP_DEVICE_INTERFACE_DATA infodata;
378
379 // Search for the Garmin Device Interface Class
380 hdevinfo = SetupDiGetClassDevs((GUID *)&GARMIN_GUID1, NULL, NULL,
381 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
382
383 if (hdevinfo == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
384
385 infodata.cbSize = sizeof(infodata);
386
387 bool bgarmin_unit_found =
388 (SetupDiEnumDeviceInterfaces(hdevinfo, NULL, (GUID *)&GARMIN_GUID1, 0,
389 &infodata) != 0);
390
391 if (!bgarmin_unit_found) return false;
392
393 PSP_INTERFACE_DEVICE_DETAIL_DATA pdd = NULL;
394 SP_DEVINFO_DATA devinfo;
395
396 SetupDiGetDeviceInterfaceDetail(hdevinfo, &infodata, NULL, 0, &size, NULL);
397
398 pdd = (PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc(size);
399 pdd->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
400
401 devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
402 if (!SetupDiGetDeviceInterfaceDetail(hdevinfo, &infodata, pdd, size, NULL,
403 &devinfo)) {
404 free(pdd);
405 return false;
406 }
407
408 free(pdd);
409
410 return true;
411}
412
413HANDLE GarminProtocolHandler::garmin_usb_start() {
414 DWORD size = 0;
415
416 HDEVINFO hdevinfo;
417 SP_DEVICE_INTERFACE_DATA infodata;
418
419 // Search for the Garmin Device Interface Class
420 hdevinfo = SetupDiGetClassDevs((GUID *)&GARMIN_GUID1, NULL, NULL,
421 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
422
423 if (hdevinfo == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
424
425 infodata.cbSize = sizeof(infodata);
426
427 bool bgarmin_unit_found =
428 (SetupDiEnumDeviceInterfaces(hdevinfo, NULL, (GUID *)&GARMIN_GUID1, 0,
429 &infodata) != 0);
430
431 if (!bgarmin_unit_found) return INVALID_HANDLE_VALUE;
432
433 wxLogMessage(_T("Garmin USB Device Found"));
434
435 if ((m_usb_handle == INVALID_HANDLE_VALUE) || (m_usb_handle == 0)) {
436 PSP_INTERFACE_DEVICE_DETAIL_DATA pdd = NULL;
437 SP_DEVINFO_DATA devinfo;
438
439 SetupDiGetDeviceInterfaceDetail(hdevinfo, &infodata, NULL, 0, &size, NULL);
440
441 pdd = (PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc(size);
442 pdd->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
443
444 devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
445 if (!SetupDiGetDeviceInterfaceDetail(hdevinfo, &infodata, pdd, size, NULL,
446 &devinfo)) {
447 wxLogMessage(
448 _T(" SetupDiGetDeviceInterfaceDetail failed for Garmin Device..."));
449 free(pdd);
450 return INVALID_HANDLE_VALUE;
451 }
452
453 /* Whew. All that just to get something we can open... */
454 // wxString msg;
455 // msg.Printf(_T("Windows GUID for interface is
456 // %s"),pdd->DevicePath); wxLogMessage(msg);
457
458 if (m_bneed_int_reset) {
459 ResetGarminUSBDriver();
460 m_bneed_int_reset = false;
461 }
462
463 m_usb_handle = CreateFile(pdd->DevicePath, GENERIC_READ | GENERIC_WRITE, 0,
464 NULL, OPEN_EXISTING, 0, NULL);
465
466 if (m_usb_handle == INVALID_HANDLE_VALUE) {
467 wxString msg;
468 msg.Printf(_T(" (usb) CreateFile on '%s' failed"), pdd->DevicePath);
469 wxLogMessage(msg);
470 }
471
472 /*
473 DEV_BROADCAST_HANDLE filterData;
474 filterData.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
475 filterData.dbch_devicetype = DBT_DEVTYP_HANDLE;
476 filterData.dbch_reserved = 0;
477 filterData.dbch_handle = m_usb_handle; // file handle used in
478 call to RegisterDeviceNotification filterData.dbch_hdevnotify = 0; //
479 returned from RegisterDeviceNotification
480
481 HDEVNOTIFY m_hDevNotify = RegisterDeviceNotification( GetHWND(),
482 &filterData, DEVICE_NOTIFY_WINDOW_HANDLE);
483 */
484
485 free(pdd);
486 }
487
488 m_max_tx_size = 0;
489
490 if (!DeviceIoControl(m_usb_handle, IOCTL_GARMIN_USB_BULK_OUT_PACKET_SIZE,
491 NULL, 0, &m_max_tx_size, GARMIN_USB_INTERRUPT_DATA_SIZE,
492 &size, NULL)) {
493 wxLogMessage(_T(" Couldn't get Garmin USB packet size."));
494 CloseHandle(m_usb_handle);
495 m_usb_handle = INVALID_HANDLE_VALUE;
496 return INVALID_HANDLE_VALUE;
497 }
498
499 if (!gusb_syncup()) {
500 CloseHandle(m_usb_handle);
501 m_usb_handle = INVALID_HANDLE_VALUE;
502 }
503
504 return m_usb_handle;
505}
506
507bool GarminProtocolHandler::gusb_syncup(void) {
508 static int unit_number;
509 static const char oinit[12] = {0, 0, 0, 0, GUSB_SESSION_START, 0, 0, 0,
510 0, 0, 0, 0};
511 garmin_usb_packet iresp;
512 int i;
513
514 /*
515 * This is our first communication with the unit.
516 */
517
518 m_receive_state = rs_fromintr;
519
520 for (i = 0; i < 25; i++) {
521 le_write16(&iresp.gusb_pkt.pkt_id[0], 0);
522 le_write32(&iresp.gusb_pkt.datasz[0], 0);
523 le_write32(&iresp.gusb_pkt.databuf[0], 0);
524
525 if (gusb_cmd_send((const garmin_usb_packet *)oinit, sizeof(oinit))) {
526 gusb_cmd_get(&iresp, sizeof(iresp));
527
528 if ((le_read16(iresp.gusb_pkt.pkt_id) == GUSB_SESSION_ACK) &&
529 (le_read32(iresp.gusb_pkt.datasz) == 4)) {
530 // unsigned serial_number =
531 // le_read32(iresp.gusb_pkt.databuf);
532 // garmin_unit_info[unit_number].serial_number =
533 // serial_number;
534 // gusb_id_unit(&garmin_unit_info[unit_number]);
535
536 unit_number++;
537
538 wxLogMessage(_T("Successful Garmin USB syncup."));
539 return true;
540 ;
541 }
542 }
543 }
544 wxLogMessage(_T(" Unable to establish Garmin USB syncup."));
545 return false;
546}
547
548int GarminProtocolHandler::gusb_cmd_send(const garmin_usb_packet *opkt,
549 size_t sz) {
550 unsigned int rv;
551
552 unsigned char *obuf = (unsigned char *)&opkt->dbuf[0];
553
554 rv = gusb_win_send(opkt, sz);
555
556 /*
557 * Recursion, when used in a disciplined way, can be our friend.
558 *
559 * The Garmin protocol requires that packets that are exactly
560 * a multiple of the max tx size be followed by a zero length
561 * packet. Do that here so we can see it in debugging traces.
562 */
563
564 if (sz && !(sz % m_max_tx_size)) {
565 wxLogMessage(_T("win_send_call1"));
566 gusb_win_send(opkt, 0);
567 wxLogMessage(_T("win_send_ret1"));
568 }
569
570 return (rv);
571}
572
573int GarminProtocolHandler::gusb_cmd_get(garmin_usb_packet *ibuf, size_t sz) {
574 int rv = 0;
575 unsigned char *buf = (unsigned char *)&ibuf->dbuf[0];
576 int orig_receive_state;
577top:
578 orig_receive_state = m_receive_state;
579 switch (m_receive_state) {
580 case rs_fromintr:
581 rv = gusb_win_get(ibuf, sz);
582 break;
583 case rs_frombulk:
584 rv = gusb_win_get_bulk(ibuf, sz);
585 break;
586 }
587
588 /* Adjust internal state and retry the read */
589 if ((rv > 0) && (ibuf->gusb_pkt.pkt_id[0] == GUSB_REQUEST_BULK)) {
590 m_receive_state = rs_frombulk;
591 goto top;
592 }
593 /*
594 * If we were reading from the bulk pipe and we just got
595 * a zero request, adjust our internal state.
596 * It's tempting to retry the read here to hide this "stray"
597 * packet from our callers, but that only works when you know
598 * there's another packet coming. That works in every case
599 * except the A000 discovery sequence.
600 */
601 if ((m_receive_state == rs_frombulk) && (rv <= 0)) {
602 m_receive_state = rs_fromintr;
603 }
604
605 return rv;
606}
607
608int GarminProtocolHandler::gusb_win_get(garmin_usb_packet *ibuf, size_t sz) {
609 DWORD rxed = GARMIN_USB_INTERRUPT_DATA_SIZE;
610 unsigned char *buf = (unsigned char *)&ibuf->dbuf[0];
611 int tsz = 0;
612
613 while (sz) {
614 /* The driver wrongly (IMO) rejects reads smaller than
615 * GARMIN_USB_INTERRUPT_DATA_SIZE
616 */
617 if (!DeviceIoControl(m_usb_handle, IOCTL_GARMIN_USB_INTERRUPT_IN, NULL, 0,
618 buf, GARMIN_USB_INTERRUPT_DATA_SIZE, &rxed, NULL)) {
619 // GPS_Serial_Error("Ioctl");
620 // fatal("ioctl\n");
621 }
622
623 buf += rxed;
624 sz -= rxed;
625 tsz += rxed;
626 if (rxed < GARMIN_USB_INTERRUPT_DATA_SIZE) break;
627 }
628 return tsz;
629}
630
631int GarminProtocolHandler::gusb_win_get_bulk(garmin_usb_packet *ibuf,
632 size_t sz) {
633 int n;
634 DWORD rsz;
635 unsigned char *buf = (unsigned char *)&ibuf->dbuf[0];
636
637 n = ReadFile(m_usb_handle, buf, sz, &rsz, NULL);
638
639 return rsz;
640}
641
642int GarminProtocolHandler::gusb_win_send(const garmin_usb_packet *opkt,
643 size_t sz) {
644 DWORD rsz;
645 unsigned char *obuf = (unsigned char *)&opkt->dbuf[0];
646
647 /* The spec warns us about making writes an exact multiple
648 * of the packet size, but isn't clear whether we can issue
649 * data in a single call to WriteFile if it spans buffers.
650 */
651 WriteFile(m_usb_handle, obuf, sz, &rsz, NULL);
652 int err = GetLastError();
653
654 // if (rsz != sz)
655 // fatal ("Error sending %d bytes. Successfully sent %ld\n", sz,
656 // rsz);
657
658 return rsz;
659}
660
661/*
662WXLRESULT GarminProtocolHandler::MSWWindowProc(WXUINT message, WXWPARAM wParam,
663WXLPARAM lParam)
664{
665 // did we process the message?
666 bool processed = false;
667
668 // the return value
669 bool rc;
670 PDEV_BROADCAST_HDR pDBHdr;
671 PDEV_BROADCAST_HANDLE pDBHandle;
672
673 // for most messages we should return 0 when we do process the message
674 rc = 0;
675
676 switch ( message )
677 {
678 case WM_DEVICECHANGE:
679 switch (wParam)
680 {
681 case DBT_DEVICEREMOVEPENDING:
682 case DBT_DEVICEREMOVECOMPLETE:
683 pDBHdr = (PDEV_BROADCAST_HDR) lParam;
684 switch (pDBHdr->dbch_devicetype)
685 case DBT_DEVTYP_HANDLE:
686 // A Device has been removed
687 // Stop the IO thread and close open handle to
688device
689
690 pDBHandle = (PDEV_BROADCAST_HANDLE) pDBHdr;
691 HANDLE target_handle = pDBHandle->dbch_handle;
692
693 wxLogMessage(_T("Garmin USB Device Removed"));
694 StopIOThread(false);
695 m_bneed_int_reset = true;
696 processed = true;
697 break;
698 }
699
700 break;
701
702 }
703
704 if ( !processed )
705 {
706 rc = (MSWDefWindowProc(message, wParam, lParam) != 0);
707 }
708
709 return rc;
710}
711*/
712#endif
713
715
716//-------------------------------------------------------------------------------------------------------------
717//
718// Garmin Serial Port Worker Thread
719//
720// This thread manages reading the positioning data stream from the declared
721// Garmin GRMN Mode serial device
722//
723//-------------------------------------------------------------------------------------------------------------
724//FIXME (dave) implement using comm
725#if 0
726GARMIN_Serial_Thread::GARMIN_Serial_Thread(GarminProtocolHandler *parent,
727 DataStream *GParentStream,
728 wxEvtHandler *MessageTarget,
729 wxString port) {
730 m_parent = parent; // This thread's immediate "parent"
731 m_parent_stream = GParentStream;
732 m_pMessageTarget = MessageTarget;
733 m_port = port;
734
735 Create();
736}
737
738GARMIN_Serial_Thread::~GARMIN_Serial_Thread(void) {}
739
740// Entry Point
741void *GARMIN_Serial_Thread::Entry() {
742 // m_parent->SetSecThreadActive(); // I am alive
743 m_bdetected = false;
744 m_bconnected = false;
745
746 bool not_done = true;
747 wxDateTime last_rx_time;
748
749#ifdef USE_GARMINHOST
750 // The main loop
751
752 while ((not_done) && (m_parent->m_Thread_run_flag > 0)) {
753 if (TestDestroy()) {
754 not_done = false; // smooth exit
755 goto thread_exit;
756 }
757
758 while (!m_bdetected) {
759 // Try to init the port once
760 int v_init = Garmin_GPS_Init(m_port);
761 if (v_init < 0) { // Open failed, so sleep and try again
762 for (int i = 0; i < 4; i++) {
763 wxSleep(1);
764 if (TestDestroy()) goto thread_exit;
765 if (!m_parent->m_Thread_run_flag) goto thread_exit;
766 }
767 } else
768 m_bdetected = true;
769 } // while not detected
770
771 // Detected OK
772
773 // Start PVT packet transmission
774 if (!m_bconnected) {
775 if (!Garmin_GPS_PVT_On(m_port)) {
776 m_bdetected = false; // error, would not accept PVT On
777 m_bconnected = false;
778 } else
779 m_bconnected = true;
780 }
781
782 if (m_bconnected) {
783 D800_Pvt_Data_Type_Aligned *ppvt = &mypvt;
784 int ret = Garmin_GPS_GetPVT(&ppvt);
785 if (ret > 0) {
786 if ((mypvt.fix) >= 2 && (mypvt.fix <= 5)) {
787 // Synthesize an NMEA GMRMC message
788 SENTENCE snt;
789 NMEA0183 oNMEA0183;
790 oNMEA0183.TalkerID = _T ( "GM" );
791
792 if (mypvt.lat < 0.)
793 oNMEA0183.Rmc.Position.Latitude.Set(-mypvt.lat, _T ( "S" ));
794 else
795 oNMEA0183.Rmc.Position.Latitude.Set(mypvt.lat, _T ( "N" ));
796
797 if (mypvt.lon < 0.)
798 oNMEA0183.Rmc.Position.Longitude.Set(-mypvt.lon, _T ( "W" ));
799 else
800 oNMEA0183.Rmc.Position.Longitude.Set(mypvt.lon, _T ( "E" ));
801
802 /* speed over ground */
803 double sog =
804 sqrt(mypvt.east * mypvt.east + mypvt.north * mypvt.north) * 3.6 /
805 1.852;
806 oNMEA0183.Rmc.SpeedOverGroundKnots = sog;
807
808 /* course over ground */
809 double course = atan2(mypvt.east, mypvt.north);
810 if (course < 0) course += 2 * PI;
811 double cog = course * 180 / PI;
812 oNMEA0183.Rmc.TrackMadeGoodDegreesTrue = cog;
813
814 oNMEA0183.Rmc.IsDataValid = NTrue;
815
816 oNMEA0183.Rmc.Write(snt);
817 wxString message = snt.Sentence;
818
819 if (m_pMessageTarget) {
820 OCPN_DataStreamEvent Nevent(wxEVT_OCPN_DATASTREAM, 0);
821 wxCharBuffer buffer = message.ToUTF8();
822 if (buffer.data()) {
823 Nevent.SetNMEAString(buffer.data());
824 Nevent.SetStream(m_parent_stream);
825
826 m_pMessageTarget->AddPendingEvent(Nevent);
827 }
828 }
829
830 last_rx_time = wxDateTime::Now();
831 }
832 } else {
833 wxDateTime now = wxDateTime::Now();
834 if (last_rx_time.IsValid()) {
835 wxTimeSpan delta_time = now - last_rx_time;
836 if (delta_time.GetSeconds() > 5) {
837 m_bdetected = false;
838 m_bconnected = false;
839 Garmin_GPS_ClosePortVerify();
840 }
841 }
842 }
843 }
844 } // the big while...
845
846thread_exit:
847
848 Garmin_GPS_PVT_Off(m_port);
849 Garmin_GPS_ClosePortVerify();
850
851#else //#ifdef USE_GARMINHOST
852
853 while ((not_done) && (m_parent->m_Thread_run_flag > 0)) {
854 wxSleep(1);
855 if (TestDestroy()) {
856 not_done = false; // smooth exit
857 goto thread_exit;
858 }
859 }
860
861thread_exit:
862
863#endif //#ifdef USE_GARMINHOST
864
865 m_parent->m_Thread_run_flag = -1; // in GarminProtocolHandler
866 return 0;
867}
868#endif
869
870//-------------------------------------------------------------------------------------------------------------
871// GARMIN_USB_Thread Implementation
872//-------------------------------------------------------------------------------------------------------------
873// FIXME (dave) implement using comm
874#if 0
875GARMIN_USB_Thread::GARMIN_USB_Thread(GarminProtocolHandler *parent,
876 DataStream *GParentStream,
877 wxEvtHandler *MessageTarget,
878 unsigned int device_handle,
879 size_t max_tx_size) {
880 m_parent = parent; // This thread's immediate "parent"
881 m_parent_stream = GParentStream;
882 m_pMessageTarget = MessageTarget;
883 m_max_tx_size = max_tx_size;
884
885#ifdef __WXMSW__
886 m_usb_handle = (HANDLE)(device_handle & 0xffff);
887#endif
888
889 Create();
890}
891
892GARMIN_USB_Thread::~GARMIN_USB_Thread() {}
893
894void *GARMIN_USB_Thread::Entry() {
895 garmin_usb_packet iresp = {{0}};
896 int n_short_read = 0;
897 m_receive_state = rs_fromintr;
898
899 // Here comes the big while loop
900 while (m_parent->m_Thread_run_flag > 0) {
901 if (TestDestroy()) goto thread_prexit; // smooth exit
902
903 // Get one packet
904
905 int nr = gusb_cmd_get(&iresp, sizeof(iresp));
906
907 if (iresp.gusb_pkt.pkt_id[0] == GUSB_RESPONSE_SDR) // Satellite Data Record
908 {
909 unsigned char *t = (unsigned char *)&(iresp.gusb_pkt.databuf[0]);
910 for (int i = 0; i < 12; i++) {
911 m_sat_data[i].svid = *t++;
912 m_sat_data[i].snr = ((*t) << 8) + *(t + 1);
913 t += 2;
914 m_sat_data[i].elev = *t++;
915 m_sat_data[i].azmth = ((*t) << 8) + *(t + 1);
916 t += 2;
917 m_sat_data[i].status = *t++;
918 }
919
920 m_nSats = 0;
921 for (int i = 0; i < 12; i++) {
922 if (m_sat_data[i].svid != 255) m_nSats++;
923 }
924
925 // Synthesize an NMEA GMGSV message
926 SENTENCE snt;
927 NMEA0183 oNMEA0183;
928 oNMEA0183.TalkerID = _T ( "GM" );
929 oNMEA0183.Gsv.SatsInView = m_nSats;
930
931 oNMEA0183.Gsv.Write(snt);
932 wxString message = snt.Sentence;
933
934 if (m_pMessageTarget) {
935 OCPN_DataStreamEvent Nevent(wxEVT_OCPN_DATASTREAM, 0);
936 wxCharBuffer buffer = message.ToUTF8();
937 if (buffer.data()) {
938 Nevent.SetNMEAString(buffer.data());
939 Nevent.SetStream(m_parent_stream);
940
941 m_pMessageTarget->AddPendingEvent(Nevent);
942 }
943 }
944 }
945
946 if (iresp.gusb_pkt.pkt_id[0] == GUSB_RESPONSE_PVT) // PVT Data Record
947 {
948 D800_Pvt_Data_Type *ppvt =
949 (D800_Pvt_Data_Type *)&(iresp.gusb_pkt.databuf[0]);
950
951 if ((ppvt->fix) >= 2 && (ppvt->fix <= 5)) {
952 // Synthesize an NMEA GMRMC message
953 SENTENCE snt;
954 NMEA0183 oNMEA0183;
955 oNMEA0183.TalkerID = _T ( "GM" );
956
957 if (ppvt->lat < 0.)
958 oNMEA0183.Rmc.Position.Latitude.Set(-ppvt->lat * 180. / PI,
959 _T ( "S" ));
960 else
961 oNMEA0183.Rmc.Position.Latitude.Set(ppvt->lat * 180. / PI,
962 _T ( "N" ));
963
964 if (ppvt->lon < 0.)
965 oNMEA0183.Rmc.Position.Longitude.Set(-ppvt->lon * 180. / PI,
966 _T ( "W" ));
967 else
968 oNMEA0183.Rmc.Position.Longitude.Set(ppvt->lon * 180. / PI,
969 _T ( "E" ));
970
971 /* speed over ground */
972 double sog = sqrt(ppvt->east * ppvt->east + ppvt->north * ppvt->north) *
973 3.6 / 1.852;
974 oNMEA0183.Rmc.SpeedOverGroundKnots = sog;
975
976 /* course over ground */
977 double course = atan2(ppvt->east, ppvt->north);
978 if (course < 0) course += 2 * PI;
979 double cog = course * 180 / PI;
980 oNMEA0183.Rmc.TrackMadeGoodDegreesTrue = cog;
981
982 oNMEA0183.Rmc.IsDataValid = NTrue;
983
984 oNMEA0183.Rmc.Write(snt);
985 wxString message = snt.Sentence;
986
987 if (m_pMessageTarget) {
988 OCPN_DataStreamEvent Nevent(wxEVT_OCPN_DATASTREAM, 0);
989 wxCharBuffer buffer = message.ToUTF8();
990 if (buffer.data()) {
991 Nevent.SetNMEAString(buffer.data());
992 Nevent.SetStream(m_parent_stream);
993
994 m_pMessageTarget->AddPendingEvent(Nevent);
995 }
996 }
997 }
998 }
999 }
1000thread_prexit:
1001 m_parent->m_Thread_run_flag = -1;
1002 return 0;
1003}
1004
1005int GARMIN_USB_Thread::gusb_cmd_get(garmin_usb_packet *ibuf, size_t sz) {
1006 int rv = 0;
1007 unsigned char *buf = (unsigned char *)&ibuf->dbuf[0];
1008 int orig_receive_state;
1009top:
1010 orig_receive_state = m_receive_state;
1011 switch (m_receive_state) {
1012 case rs_fromintr:
1013 rv = gusb_win_get(ibuf, sz);
1014 break;
1015 case rs_frombulk:
1016 rv = gusb_win_get_bulk(ibuf, sz);
1017 break;
1018 }
1019
1020 /* Adjust internal state and retry the read */
1021 if ((rv > 0) && (ibuf->gusb_pkt.pkt_id[0] == GUSB_REQUEST_BULK)) {
1022 m_receive_state = rs_frombulk;
1023 goto top;
1024 }
1025 /*
1026 * If we were reading from the bulk pipe and we just got
1027 * a zero request, adjust our internal state.
1028 * It's tempting to retry the read here to hide this "stray"
1029 * packet from our callers, but that only works when you know
1030 * there's another packet coming. That works in every case
1031 * except the A000 discovery sequence.
1032 */
1033 if ((m_receive_state == rs_frombulk) && (rv <= 0)) {
1034 m_receive_state = rs_fromintr;
1035 }
1036
1037 return rv;
1038}
1039
1040int GARMIN_USB_Thread::gusb_win_get(garmin_usb_packet *ibuf, size_t sz) {
1041 int tsz = 0;
1042#ifdef __WXMSW__
1043 DWORD rxed = GARMIN_USB_INTERRUPT_DATA_SIZE;
1044 unsigned char *buf = (unsigned char *)&ibuf->dbuf[0];
1045
1046 while (sz) {
1047 /* The driver wrongly (IMO) rejects reads smaller than
1048 * GARMIN_USB_INTERRUPT_DATA_SIZE
1049 */
1050 if (!DeviceIoControl(m_usb_handle, IOCTL_GARMIN_USB_INTERRUPT_IN, NULL, 0,
1051 buf, GARMIN_USB_INTERRUPT_DATA_SIZE, &rxed, NULL)) {
1052 // GPS_Serial_Error("Ioctl");
1053 // fatal("ioctl\n");
1054 }
1055
1056 buf += rxed;
1057 sz -= rxed;
1058 tsz += rxed;
1059 if (rxed < GARMIN_USB_INTERRUPT_DATA_SIZE) break;
1060 }
1061
1062#endif
1063 return tsz;
1064}
1065
1066int GARMIN_USB_Thread::gusb_win_get_bulk(garmin_usb_packet *ibuf, size_t sz) {
1067 int n;
1068 int ret_val = 0;
1069#ifdef __WXMSW__
1070 DWORD rsz;
1071 unsigned char *buf = (unsigned char *)&ibuf->dbuf[0];
1072
1073 n = ReadFile(m_usb_handle, buf, sz, &rsz, NULL);
1074 ret_val = rsz;
1075#endif
1076
1077 return ret_val;
1078}
1079#endif