1

I try to read PVT(Position,Velocity,Time) record from an Etrex 10 Garmin GPS that its USB port is connected to Android tablet(Samsung Galaxy Tab S,Model SM-T705 supporting USB host function).First I used a Windows application to simulate this communication and I monitored USB sent/received packets.According to Garmin documentation(Garmin GPS Interface Specification,USB Addendum),these data packets was sent and received successfully(all numbers are in hexadecimal):

(PC to GPS,start session packet) 00 00 00 00 05 00 00 00 00 00 00 00

(GPS to PC,Acknowledge starting session) 00 00 00 00 06 00 00 00 04 00 00 00 0b ba aa e7

this is part of section 6 of this document that show how host and device(GPS) do communication : ..........................

6-Sample Communication Session

This section provides a sample communication session between a USB host and a Garmin USB GPS. Messages below are presented in chronological order. All data is presented in HEX format. Bulk Out (USB Transport Layer - Start Session) The host sends this first to the Garmin USB device, to tell it to prepare for a transfer. 00 00 00 00 05 00 00 00 00 00 00 00 Interrupt In (USB Transport Layer - Session Started) The device responds to the host, letting the host know the device is prepared for a conversation. 00 00 00 00 06 00 00 00 04 00 00 00 37 C4 E4 AB

...........................

I tried to code this communication in an Android application.In Android codes I can recognize GPS device when connected to tablet.there are 3 end-points.One Bulk_Out ,another Bulk-in and the third one Interrupt-in.When I send start session packet over Bulk-out end point,I don't receive Acknowledge packet over Interrupt(or even Bulk_in) endpoint.here is the code in Xamarin Studio using MONO library :

public class MainActivity : Activity
{
private UsbManager m_USBManager = null;
private UsbDevice m_GPS = null;
private UsbInterface m_USBInterface = null;
private UsbEndpoint m_BulkOut = null;
private UsbEndpoint m_BulkIn = null;
private UsbEndpoint m_InterruptIn = null;
private UsbDeviceConnection m_USBConnection = null;

public static string ACTION_USB_PERMISSION = "GarminEtrex10.USB_PERMISSION";

private TextView m_TxMsg;
private ListView m_LvTxRxRows;

private USBReceiver m_USBRx;
PendingIntent m_PermissionIntent;

public List<TxRxRow> m_TxRxRows = new List<TxRxRow> ();

protected override void OnCreate (Bundle bundle)
{
    base.OnCreate (bundle);

    SetContentView (Resource.Layout.Main);

    this.m_PermissionIntent = PendingIntent.GetBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);

    Button button = FindViewById<Button> (Resource.Id.myButton);
    this.m_TxMsg = FindViewById<TextView> (Resource.Id.TxMsg);
    this.m_LvTxRxRows = FindViewById<ListView> (Resource.Id.LvTxRx);

    this.m_USBManager = (UsbManager) this.GetSystemService (Context.UsbService);
    this.m_USBRx = new USBReceiver (this, this.m_USBManager);
    IntentFilter filter = new IntentFilter (ACTION_USB_PERMISSION);
    RegisterReceiver (this.m_USBRx, filter);

    button.Click += new EventHandler (this.ButtonClicked);
}

protected override void OnResume ()
{
    base.OnResume ();

    bool bHasPermission = false;

    if (this.m_USBManager != null)
    {
        foreach (string strKey in this.m_USBManager.DeviceList.Keys)
        {
            UsbDevice usbd = this.m_USBManager.DeviceList [strKey];
            if (usbd.VendorId == 2334)//Garmin product Id
            {
                this.m_GPS = usbd;
                break;
            }
        }
    }
    else
        this.m_TxMsg.Text = "USB Manager is null";
    if (this.m_GPS != null) 
    {
        this.m_USBManager.RequestPermission (this.m_GPS, this.m_PermissionIntent);
        bHasPermission = this.m_USBManager.HasPermission (this.m_GPS);
        this.m_TxMsg.Text = "Garmin GPS found.";
        if (bHasPermission)
            this.m_TxMsg.Text += " and have permission to use it.";
        else
            this.m_TxMsg.Text += " but not have permission to use it.";
    }
    else
        this.m_TxMsg.Text = "No GPS found.";
    if (bHasPermission) 
    {
        this.m_USBInterface = this.m_GPS.GetInterface (0);
        if (this.m_USBInterface != null) 
        {

            for (int i = 0; i < this.m_USBInterface.EndpointCount; i++)
            {
                UsbEndpoint ep = this.m_USBInterface.GetEndpoint (i);
                if (ep.Type == UsbAddressing.XferBulk && ep.Direction == UsbAddressing.Out && this.m_BulkOut == null)
                    this.m_BulkOut = ep;
                if (ep.Type == UsbAddressing.XferBulk && ep.Direction == UsbAddressing.In && this.m_BulkIn == null)
                    this.m_BulkIn = ep;
                if (ep.Type == UsbAddressing.XferInterrupt && ep.Direction == UsbAddressing.DirMask && this.m_InterruptIn == null) 
                    this.m_InterruptIn = ep;
            }
        }
    }
}

public void PopulateListView(byte[] bytes, byte port)
{
    TxRxRow row = new TxRxRow (port, bytes);
    this.m_TxRxRows.Add (row);
    TxRxAdapter adp = new TxRxAdapter (this, this.m_TxRxRows);
    this.m_LvTxRxRows.Adapter = adp;
}

public void SetStatus(string strMsg)
{
    this.m_TxMsg.Text = strMsg;
}

public void SetDevice(UsbDevice gps, UsbInterface gpsInterface, UsbDeviceConnection gpsConnection, UsbEndpoint gpsBulkout,UsbEndpoint gpsBultIn,UsbEndpoint gpsInterruptIn)
{
    if (this.m_BulkIn != null)
        return;
    this.m_GPS = gps;
    this.m_USBConnection = gpsConnection;
    this.m_USBInterface = gpsInterface;
    this.m_InterruptIn = gpsInterruptIn;
    this.m_BulkIn = gpsBultIn;
    this.m_BulkOut = gpsBulkout;
}
private void ButtonClicked(object sender, EventArgs e)
{
    if (this.m_BulkOut == null || (this.m_BulkIn == null && this.m_InterruptIn == null)) 
    {
        Toast.MakeText (this, "Could not find right endpoints", ToastLength.Long).Show ();
        return;
    }
    if(this.m_USBConnection ==null)
        this.m_USBConnection = this.m_USBManager.OpenDevice (this.m_GPS);
    if (this.m_USBConnection == null) 
    {
        Toast.MakeText (this, "Can not open USB device", ToastLength.Long).Show ();
        return;
    }

    if (!this.m_USBConnection.ClaimInterface (this.m_USBInterface, true)) 
    {
        Toast.MakeText (this, "Can not claim interface", ToastLength.Long).Show ();
        return;
    }

    int sentBytesCount = 0;

    #region Send Start_Session Packet
    byte[] obuffer = new byte[12];
    for(int i = 0; i < 12; i++)
        obuffer[i] = 0;
    obuffer[4]=5;

    this.m_TxMsg.Text = "Sending Start session packet....";
    sentBytesCount += this.m_USBConnection.BulkTransfer (this.m_BulkOut, obuffer, 12, 100);

    if (sentBytesCount >= 0) 
    {
        this.m_TxMsg.Text = "Sent bytes = "+sentBytesCount.ToString();
        this.PopulateListView(obuffer,1);
        this.m_USBRx.ReceiveFlag = true;
        this.m_USBConnection.RequestWaitAsync();
    }
    else
    {
        Toast.MakeText (this, "Sent bytes for start session packet = 0.operation failed", ToastLength.Long).Show ();
    }
    #endregion
}
}

And this is the braodcast receiver class

public class USBReceiver : BroadcastReceiver
{
private UsbManager m_USBMngr = null;
private Context m_Activity = null;
private UsbEndpoint m_BulkIN = null;
private UsbEndpoint m_InterruptIN = null;
private UsbDeviceConnection m_USBConnection=null;

public bool ReceiveFlag = false;

public USBReceiver(Context activity, UsbManager mngr) 
{
    this.m_Activity = activity;
    this.m_USBMngr = mngr;
}
public override void OnReceive (Context context, Intent intent)
{
    String action = intent.Action;
    (this.m_Activity as MainActivity).SetStatus ("Receiving activity=" + intent.Action + "...");
    if (action.Equals (MainActivity.ACTION_USB_PERMISSION)) 
    {
        lock (this) 
        {
            UsbDevice device = (UsbDevice)intent.GetParcelableExtra (UsbManager.ExtraDevice);
            bool bHasPermission = intent.GetBooleanExtra (UsbManager.ExtraPermissionGranted, false);
            //For the first time initialize MainActivity local parameters
            if (this.m_USBConnection == null) 
            {
                if (device != null && bHasPermission) 
                {
                    if (device.VendorId != 2334)
                        return;
                    this.m_USBConnection = this.m_USBMngr.OpenDevice (device);
                    UsbInterface ui = device.GetInterface (0);
                    UsbEndpoint epBulkOut = null;
                    for (int i = 0; i < ui.EndpointCount; i++) 
                    {
                        UsbEndpoint ep = ui.GetEndpoint (i);
                        if (ep.Type == UsbAddressing.XferInterrupt && ep.Direction == UsbAddressing.In)
                            this.m_InterruptIN = ep;
                        if (ep.Type == UsbAddressing.XferBulk && ep.Direction == UsbAddressing.In)
                            this.m_BulkIN = ep;
                        if (ep.Type == UsbAddressing.XferBulk && ep.Direction == UsbAddressing.Out)
                            epBulkOut = ep;
                    }
                    (this.m_Activity as MainActivity).SetDevice (device, ui, this.m_USBConnection, epBulkOut, this.m_BulkIN, this.m_InterruptIN);
                }
            }
            if (this.m_BulkIN != null) 
            {
                int maxPacketSize = this.m_BulkIN.MaxPacketSize;
                byte[] buff = new byte[maxPacketSize];
                int bytesRead = this.m_USBConnection.BulkTransfer (this.m_BulkIN, buff, maxPacketSize, 4000);
                if (bytesRead > 0) 
                {
                    (this.m_Activity as MainActivity).PopulateListView (buff, 2);
                    ReceiveFlag = false;
                }
            }
            if (this.m_InterruptIN != null) 
            {
                UsbRequest req = new UsbRequest ();
                if (req.Initialize (this.m_USBConnection, this.m_InterruptIN)) 
                {

                    int maxsize = this.m_InterruptIN.MaxPacketSize;
                    Java.Nio.ByteBuffer buff = Java.Nio.ByteBuffer.Allocate (maxsize);
                    if (req.Queue (buff, maxsize)) 
                    {
                        if (this.m_USBConnection.RequestWait () == req)
                        {
                            byte[] bb = new byte[maxsize];
                            buff.Get (bb, 0, buff.Remaining ());
                            if (bb.Length > 0) 
                            {
                                (this.m_Activity as MainActivity).PopulateListView (bb, 3);
                            }
                        }
                    }
                }
            }
        }
    }
}
}

when I connect GPS to tablet and run application,fist I receive some packets with 00 content,then I tap command button and application sends start-session-packet,the BulkTransfer function returns 12 means it sent 12 bytes successfully but after that no any received byte in broad cast class.The received zero bytes at the start of application show that broadcast calss works in a rigth way,but I can't find why no acknowlege from GPS.It seems that GPS doesn't receive the command bytes even function returns a number greater than zero.Should I send any command over Control endpoint to GPS?there is no any documentation from Garmin about this.Can you tell me what's wrong with this code or procedure? Any help or guide line is highly appreciated

here is a snap shot of android app,as I described in comment at 2016/9/4 enter image description here

and this is the code I acheived this step:

        private void ButtonClicked(object sender, EventArgs e)
    {
        if (this.m_BulkOut == null || (this.m_BulkIn == null && this.m_InterruptIn == null)) 
        {
            Toast.MakeText (this, "Could not find right endpoints", ToastLength.Long).Show ();
            return;
        }

        if (this.m_USBConnection == null) {
            try {
                this.m_USBConnection = this.m_USBManager.OpenDevice (this.m_GPS);
            } catch {
            }
        }

        if (this.m_USBConnection == null) 
        {
            Toast.MakeText (this, "Can not open USB device", ToastLength.Long).Show ();
            return;
        }

        if (!this.m_USBConnection.ClaimInterface (this.m_USBInterface, true)) 
        {
            Toast.MakeText (this, "Can not claim interface", ToastLength.Long).Show ();
            return;
        } 
        else
            this.PopulateListView (null, (byte)USBOpCode.ClaimInterface);

        int sentBytesCount = 0;

        #region Send Start_Session Packet
        byte[] obuffer = new byte[12];
        byte[] ibuffer = new byte[12];
        for(int i = 0; i < 12; i++)
        {
            obuffer[i] = 0;
            ibuffer[i] = 0;
        }
        obuffer[4]=5;

        UsbRequest req=new UsbRequest();
        if(req.Initialize(this.m_USBConnection,this.m_InterruptIn))
        {
            this.PopulateListView(null,(byte)USBOpCode.InitializeRequest);
            ByteBuffer buff=ByteBuffer.Wrap(ibuffer,0,12);
                //.Allocate(12);
            if(req.Queue(buff, 12))
            {
                this.PopulateListView(null,(byte)USBOpCode.QueueRequest);

                sentBytesCount += this.m_USBConnection.BulkTransfer (this.m_BulkOut, obuffer, obuffer.Length, 100);
                if(sentBytesCount>0)
                    this.PopulateListView(obuffer,(byte)USBOpCode.BultTransferOut);
                sentBytesCount=0;
                sentBytesCount += this.m_USBConnection.BulkTransfer (this.m_BulkOut, obuffer, obuffer.Length, 100);
                if(sentBytesCount>0)
                    this.PopulateListView(obuffer,(byte)USBOpCode.BultTransferOut);
                sentBytesCount=0;
                sentBytesCount += this.m_USBConnection.BulkTransfer (this.m_BulkOut, obuffer, obuffer.Length, 100);
                if(sentBytesCount>0)
                    this.PopulateListView(obuffer,(byte)USBOpCode.BultTransferOut);

                if(this.m_USBConnection.RequestWait() == req)
                    this.PopulateListView(null,(byte)USBOpCode.RequestWait);
                if(true)//buff.Position() > 0)
                {

                    //buff.Get(ibuffer,0,12);
                    this.PopulateListView(ibuffer,(byte)USBOpCode.InterruptIn_Received);
                }
            }
            req.Close();
        }
        this.m_USBConnection.ReleaseInterface(this.m_USBInterface);
        this.PopulateListView(null,(byte)USBOpCode.ReleaseInterface);
        return;

}

enter image description here

enter image description here

vimuth
  • 5,064
  • 33
  • 79
  • 116
R1349
  • 119
  • 5
  • 19
    @Reza, please ask to have your accounts merged so that you can edit your own post. You can do this via the [contact us](http://stackoverflow.com/contact) link at the bottom of the page. – Glorfindel Sep 01 '16 at 07:54

1 Answers1

1

Garmin devices are a bit tricky on linux and for sure on android. This is another case of (american) business strategy (closed source, ugly documentation,...)

See this: http://wiki.openstreetmap.org/wiki/USB_Garmin_on_GNU/Linux

and on linux you need a kernel driver called 'garmin' lsmod | grep garmin http://northwestspatial.com/wp/?p=162

Here is an app for a similar problem, maybe you can contact the author https://play.google.com/store/apps/details?id=com.carlopescio.sportablet

ralf htp
  • 9,149
  • 4
  • 22
  • 34
  • ralf I don't know much about linux and i found these softwares like gpsBabel use garmin_drivers for the operating system that they run under it.But I couldn't find any garmin gps driver for Android and I think my problem is here.As I understood "grmnusb.sys" (garmin driver for Windows) does some initializes that make gps ready to communicate with end_user programs.there is no document that says what are these initilizes.I used USBLyzer to monitor sent/received packets between laptop-gps but no success to find what these initialize-data are.any other idea? – R1349 Sep 01 '16 at 07:13
  • I have edited question and attached some screen shots that show sent/received packets when my windows application runs successfully.as you can see tere is not much information about those rows which are pnp-type (some of them generated by grmnusb.sys). – R1349 Sep 01 '16 at 07:31
  • here is the source code of the garmin_gps driver for linux: **https://searchcode.com/codesearch/view/2063852/** android OS is linux. if you want to add new kernel modules like *garmin_gps* you have to compile your own android and this is really complicated. if you decide to compile your own android you can add other useful utilities beside the busybox stuff like *hcitool* and so on... – ralf htp Sep 01 '16 at 16:57
  • there are other apps like Uploader for Garmin, i think they use the mass storage ability of the garmin so they access the internal memory of the garmin. i think you do not need a new kernel module you have to initialize the communication with the garmin correctly. maybe the key is in the garmin_gps driver source code (searchcode.com/codesearch/view/2063852). maybe contact the author of the Uploader for Garmin app if he is willing to share information https://play.google.com/store/apps/details?id=com.carlopescio.uploader&hl=de the e-mail is on the bottom of this page – ralf htp Sep 01 '16 at 17:20
  • ralf,i could communicate with GPS finally thanks to you and the link of linux garmin gps drvier you put here,but I only receive 0 in my buffer.If i send right acknowledge packet gps answer me on interrupt_in endpoint but what I read from it is all zero bytes.if I send wrong packet,no any answer from gps.I'm sure I'm quite close to final answer and sure there is something wrong with my code,and I must find it.I edited the first question and added a screen shot showing this communication.I included the steps I pass in codes in listview rows. – R1349 Sep 04 '16 at 10:27
  • After almost one year pending,I returned to this project last month with some more experiences in Android programming.I found there is something wrong with Xamarin usb library from this post:https://stackoverflow.com/questions/28137280/how-to-grant-permission-to-open-usb-device-with-usb-manager-opendevice-always/28218043#28218043 so finally wrote library in java/eclipse andit worked for me.There is 2 snapshot of tablet screen above the question. – R1349 Aug 17 '17 at 04:45