Changing USB mode of device

I have a USB 3G modem which does the “flip flop” - without drivers, it acts as a USB mass storage device until it’s sent the “mode switch” request, after which it shows up as a virtual COM/serial device.

It successfully initializes as a Mass Storage device via USBHostController, but I’m not sure how I can send it the “mode switch” command. I tried casting the mode switch class as a generic device, but I get an invalid cast exception.

The usb_modeswitch.c (from Linux) data is as follows:

MessageEndpoint = 0x05
MessageContent = “55534243904ed68a24000000800008ff024445564348470000000000000000”

Here’s my test code:

        // perform mode switch
        if (device.TYPE == USBH_DeviceType.MassStorage)
        {
            Debug.Print("Setting up modem...");
            USBH_Descriptors.Configuration cd = ((USBH_RawDevice)device).GetConfigurationDescriptors(0);

            byte[] init_string = { 85, 83, 66, 67, 144, 78, 214, 138, 36, 0, 0, 0, 128, 0, 8, 255, 2, 68, 69, 86, 67, 72, 71, 0, 0, 0, 0, 0, 0, 0, 0 };
            ((USBH_RawDevice)device).SendSetupTransfer(0x00, 0x05, cd.bConfigurationValue, 0x00, init_string, 0, init_string.Length);

Any thoughts on how to make this work?

These "flip flop"s are vendor specific. You have to look at your vendor specifications but many of them do not provide this information, so your only hope in this case is looking at the linux driver or use a usb analyzer.
Anyways, you have to create your USBH_RawDevice:
USBH_RawDevice d = new USBH_RawDevice(device)…etc
d.GetConfig…
There are several examples to use raw usb in the Projects like the NXT one and an example in library documentation.

I have the vendor-specific code from Linux’s usb_modeswitch command, so I know what’s required… just not sure how to send it. Terminology in the usb_modeswitch source code seems somewhat different than the .NETMF/GHI terminology, so I must be doing something wrong.

I tried both sending the data via SendSetupTransfer, and writing the data to the raw device’s pipe with no luck in actually switching the mode.

Maybe the parameters you give to SendSetupTransfer are not correct :think: I’m thinking more precisely at the first two parameters.

I would try something like this :

device.SendSetupTransfer(0x21, 0x09, cd.bConfigurationValue, 0x00, init_string, 0, init_string.Length);

Quite possible, 0x21/0x09 throws an exception though :slight_smile:

   #### Exception System.Exception - 0xffffffff (3) ####
    #### Message: 
    #### GHIElectronics.NETMF.USBHost.USBH_RawDevice::SendSetupTransfer [IP: 0000] ####
    #### FEZ_Cobra_Console_Application1.Program::USBHostController_DeviceConnectedEvent [IP: 00ae] ####
    #### GHIElectronics.NETMF.USBHost.USBH_DeviceConnectionEventHandler::Invoke [IP: 0000] ####
    #### GHIElectronics.NETMF.USBHost.USBHostController::nativeEventDispatcher_OnInterrupt [IP: 0037] ####
    #### GHIElectronics.NETMF.System.InternalEvent::nativeEventDispatcher_OnInterrupt [IP: 0054] ####
A first chance exception of type 'System.Exception' occurred in GHIElectronics.NETMF.USBHost.dll
An unhandled exception of type 'System.Exception' occurred in GHIElectronics.NETMF.USBHost.dll

I would first avoid putting this SendSetupTransfer in the DeviceConnection handler because the device may not be in a “ready” state. At least, ready to receive commands.

I’ve experienced this behaviour when I was using the Phidgets boards (which are USB boards). I had to wait some time before issuing such a command.

What I would suggest is : put the SendSetupTransfer implementation in a method that you will call yourself, like by pushing the LDR button for example. This way, you would wait say 5-6 seconds, assuming your device would be ready after that time, and then you press the LDR button and see if there’s the exception.

The original post says to use endpoint #5, so you have to look for this endpoint and send data to it.

[quote]MessageEndpoint = 0x05
MessageContent = “55534243904ed68a24000000800008ff024445564348470000000000000000”[/quote]
Try this:
u= new USBH_RawDevice(device);
USBH_Descriptors.Configuration cd = u.GetConfigurationDescriptors(0);
u.SendSetupTransfer(0x00, 0x09, cd.bConfigurationValue, 0x00);
// you have look for endpoint # 5 (bEndpointAddress must be 5), look into the “cd” in Visual Studio debugger or write a a for loop…
USBH_Descriptors.Endpoint e = cd.interfaces[some endpoint].endpoints[some endpoint];
Pipe p = u.OpenPipe(e);
p.Write(MessageContent );

Thanks for starting this topic i have learned quite a bit from it.

Bec - thanks for the suggestion. I just assumed that since we knew the type of device, it would be ready for the setup transfer… but you know what happens when you assume :slight_smile: I tried pausing for 10 seconds, but those messages still threw an exception, and the ones I was using did not work.

Mike - I’ll give this a shot sometime today, thanks for the idea. I did try writing the data to an open pipe but it was the first endpoint, and I’m not sure it had address 5.

If this works, getting the FEZ to work with this inexpensive USB modem should be fairly easy!

[italic]Bingo![/italic]

Mike’s code did it. After I write it to that endpoint, it re-initializes and I get a new DeviceConnected event with a device type 12 - a USB serial device!!

I’m now able to issue “AT” commands on the device and receive responses, unfortunately even after I receive AT’s “CONNECT” response, PPP.Connect never returns, though I’m sure this will be much easier to figure out.

Thanks Mike!

No problem but you are going to use this with PPP?
Currently, you can issue AT commands, send messages…etc but we do not provide a functionality to hookup PPP with a “custom” user interface. Look at PPP documentation, you need to specify the “physical” serial port.

Yes, that was the plan. PPP.Enable’s constructor overload accepts a USBH_Device, so I’m not sure why it would not work :slight_smile: There is no “custom” interface, once the mode is switched, it presents as USB device type 12, so I create a USBH_SerialUSB…

Also Josef said

I would think this would be the same? Otherwise, it’s unfortunate that the PPP code is not public as it sounds like it would be simple to make it work with this device.

oh, ignore my post then.

Only issue is PPP.Connect never returns… any thoughts there? :slight_smile: As I mentioned before, I’m receiving a CONNECT from the device so it should be ready, and I’m able to connect via PPP on a laptop just fine.

There is no timeout in PPP.Connect, so with it taking forever it causes problems. I’m assuming if I put it in a thread and abort the thread it will leave issues?

There is timeout PPP.Connect() but you can not change it. I forgot for how long it takes.

Can I take a look at your code? Make it as simple as possible.

Code is below. I am just playing with things at this point, so don’t be shocked about reading a byte at a time, buffer sizes, assuming the new device is a serial device, etc… I do know better :slight_smile:

The main thread is toggling a LED and sleeping while this is running, FYI.

        static void USBHostController_DeviceConnectedEvent(USBH_Device device)
        {
            // perform mode switch
            if (device.TYPE == USBH_DeviceType.MassStorage)
            {
               // run mode switch per your example here
            }
            else
            {
              // otherwise its the serial device.
               
                USBH_SerialUSB ser = new USBH_SerialUSB(device, 460800, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One);
                byte[] buf = Encoding.UTF8.GetBytes("ATDT #777\r\n");
                byte[] cbuf = new byte[1024];
                byte[] inbuf = new byte[1024];
                int bufpos = 0;
                int len = 0;
                ser.Write(buf, 0, buf.Length);

                Thread.Sleep(500);

                while (true)
                {
                    len = ser.Read(cbuf, 0, 1);

                    if (cbuf[0] != '\n')
                    {
                        inbuf[bufpos] = cbuf[0];
                        bufpos++;
                    }
                    else
                    {
                        String resp = new String(Encoding.UTF8.GetChars(inbuf));
                        
                        Debug.Print("Received: " + new String(Encoding.UTF8.GetChars(inbuf)));
                        bufpos = 0;
                        cbuf[0] = 0;

                        if (resp == "CONNECT" || resp == "CONNECT\r")
                        {
                            Debug.Print("Received CONNECT, starting PPP Session.");
                            break;
                        }   
                    }
                    
                }
                
                PPP.Enable(ser);
                Debug.Print("Connecting...");
                PPP.ConnectionStatus cs = PPP.Connect("1234567890@ alltel.net", "alltel");
                Debug.Print("Connection finished");
                switch (cs)
                {
                    case PPP.ConnectionStatus.Authentication_Faild:
                        Debug.Print("Authentication_Faild");
                        break;
                    case PPP.ConnectionStatus.Connection_Faild:
                        Debug.Print("Connection_Faild");
                        break;
                    case PPP.ConnectionStatus.Connected:
                        // PPP setting will overload the first interface settings since only one interface is supported.
                        NetworkInterface[] netif = NetworkInterface.GetAllNetworkInterfaces();
                        Debug.Print("PPP Network settings:");
                        Debug.Print("IP Address: " + netif[0].IPAddress);
                        Debug.Print("Subnet Mask: " + netif[0].SubnetMask);
                        Debug.Print("Default Getway: " + netif[0].GatewayAddress);
                        Debug.Print("DNS Server: " + netif[0].DnsAddresses[0]);
                        bool network_is_read = false;
                        break;
                    case PPP.ConnectionStatus.Disconnected:
                        Debug.Print("Disconnected");
                        break;

                }

            }
        }

You should set the WAP and some other stuff.
Take a look at ATCommander.cs. You can find it in PPP class documentation.

What modem are you using?

It’s a UM175. I’m not sure what the WAP is, when I first tried things I just used ATZ and the init string recommended for wvdial on Linux, which works on Linux.

-> WvDial: Internet dialer version 1.54.0
–> Cannot get information for serial port.
–> Initializing modem.
–> Sending: ATZ
ATZ
OK
–> Sending: ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0
ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0
OK
–> Modem initialized.
–> Sending: ATDT#777
–> Waiting for carrier.
ATDT#777
CONNECT
–> Carrier detected. Starting PPP immediately.
–> Starting pppd at Wed Aug 26 15:30:09 2009
–> pid of pppd: 9212
–> Using interface ppp0

This made no difference so I consolidated things for the example.

I noticed that your code is called in an event_handler.

 static void USBHostController_DeviceConnectedEvent(USBH_Device device)

This is wrong, because you are blocking all other system events including PPP events.
try to create an start a new thread when you have this event. and add your code in that thread.