Re: Sierra wireless Model: MC8790 qmi_wwan? Howto to test?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Bjørn Mork <bjorn@xxxxxxx> writes:

>> Because it is an built in device and it is not my own notebook, I would need a 
>> test which is easy and fast to made.(via livecd or bootable usb-stick)
>
> Not there yet, but the best would be a tool based on libqmi-glib and
> libusb, allowing you to do QMI without a driver.  It's fairly easy to
> make.  Just hasn't been done yet.  So that doesn't help you unless you
> want to write the tool yourself...

OK, I am too slow to do this properly as part of libqmi-glib, but the
attached perl script should do for a simple test.  It requires the
Device::USB perl wrapper around libusb.  
"apt-get install libdevice-usb-perl" in Debian.


Example usage with two possible QMI devices:

nemi:/tmp# lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 17ef:4807 Lenovo UVC Camera
Bus 002 Device 006: ID 1199:68a2 Sierra Wireless, Inc. 
Bus 002 Device 008: ID 19d2:1402 ZTE WCDMA Technologies MSM 

nemi:/tmp# ./qmiver.pl --vid=1199 --pid=68a2
Debugging: off
Device: 1199:68a2
Unsupported endpoint configuration on ifnum=0
Unsupported endpoint configuration on ifnum=2
Candidate: ifnum=3
claim_interface failed for if 3 - need to unbind first?
Candidate: ifnum=8
supports 14 QMI subsystems:
  QMI_CTL (1.5)
  QMI_WDS (1.12)
  QMI_DMS (1.6)
  QMI_NAS (1.8)
  QMI_QOS (1.3)
  QMI_WMS (1.3)
  QMI_PDS (1.10)
  QMI_AUTH (1.1)
  QMI_VOICE (2.1)
  QMI_CAT (2.0)
  QMI UIM (1.4)
  QMI PBM (1.4)
  QMI_SAR (1.0)
  QMI_RMS (1.0)

Note the error message for interface #3:  It is bound to the sierra
driver:

T:  Bus=02 Lev=01 Prnt=01 Port=03 Cnt=02 Dev#=  6 Spd=480  MxCh= 0
D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
P:  Vendor=1199 ProdID=68a2 Rev= 0.06
S:  Manufacturer=Sierra Wireless, Incorporated
S:  Product=MC7710
S:  SerialNumber=3581780400xxxxx
C:* #Ifs= 4 Cfg#= 1 Atr=e0 MxPwr=  0mA
I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=sierra
E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=sierra
E:  Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=sierra
E:  Ad=83(I) Atr=03(Int.) MxPS=  64 Ivl=2ms
E:  Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
I:* If#= 8 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
E:  Ad=85(I) Atr=03(Int.) MxPS=  64 Ivl=2ms
E:  Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms


I know it won't work because the interface is a serial interface, but
just to demonstrate unbinding and retesting it:

nemi:/tmp# echo 2-4:1.3 > /sys/bus/usb/devices/2-4:1.3/driver/unbind
nemi:/tmp# ./qmiver.pl --vid=1199 --pid=68a2 --if=3
Debugging: off
Device: 1199:68a2
Candidate: ifnum=3
Failed to send message - control_msg() returned -32


Testing the second (ZTE) device demonstrates that this test is just the
initial step:

nemi:/tmp# ./qmiver.pl --vid=19d2 --pid=1402
Debugging: off
Device: 19d2:1402
Unsupported endpoint configuration on ifnum=0
Unsupported endpoint configuration on ifnum=1
Candidate: ifnum=2
supports 4 QMI subsystems:
  QMI_CTL (1.3)
  QMI_WDS (1.5)
  QMI_DMS (1.2)
  QMI_NAS (1.0)
Unsupported endpoint configuration on ifnum=3


Although it responds OK with QMI_WDS support, this device will switch to
some unknown mode (crash?) when requesting a QMI_WDS client ID.  So this
device cannot work with the qmi_wwan driver.  But I believe that's an
exception.  Any device reporting QMI_WDS support with the attached
script is likely to work with qmi_wwan.


Again, sorry for the perl.  See it as a sort of reference documentation.
Anyone capable of writing C could convert the libusb specific part to a
device type in libqmi-glib :-)


Bjørn


#!/usr/bin/perl
# Copyright 2012 Bjørn Mork <bjorn@xxxxxxx>
# License: GPLv2

use strict;
use warnings;
use Getopt::Long;
use Device::USB;
use Data::Dumper;

# defaults
my %opt = (
    'verbose' => 1, 
    'debug' => 0, 
);

GetOptions(\%opt,
	   'vid=s',
	   'pid=s',
	   'if=s',
	   'debug|d+',
	   'verbose',
	   'help|h|?',
    );

&usage if ($opt{'help'} || !$opt{'vid'} || !$opt{'pid'});

my %sysname = (
    0    => "QMI_CTL",
    1    => "QMI_WDS",
    2    => "QMI_DMS",
    3    => "QMI_NAS",
    4    => "QMI_QOS",
    5    => "QMI_WMS",
    6    => "QMI_PDS",
    7    => "QMI_AUTH",
    8    => "QMI_AT",
    9    => "QMI_VOICE",
    0xa  => "QMI_CAT",
    0xb  => "QMI UIM",
    0xc  => "QMI PBM",
    0x10 => "QMI_LOC",
    0x11 => "QMI_SAR",
    0xe0 => "QMI_CAT", # duplicate!
    0xe1 => "QMI_RMS",
    0xe2 => "QMI_OMA",
    );

my $usb = Device::USB->new();
$usb->debug_mode($opt{'debug'});

my $dev = $usb->find_device(hex($opt{'vid'}), hex($opt{'pid'}));
die "Cannot find any such device: $opt{'vid'}:$opt{'pid'}\n" unless $dev;

warn "Device: ", sprintf("%04x:%04x", $dev->idVendor(), $dev->idProduct()), "\n" if $opt{'verbose'};

$dev->open();

my $cfg = $dev->config()->[0];

# cannot use ifnum as array idx as the numbering may not be consecutive...
my @intflist = grep { !$opt{'if'} || $_->[0]->bInterfaceNumber == $opt{'if'} } @{$cfg->interfaces()};
warn Dumper(\@intflist) if $opt{'debug'};

foreach (@intflist) {
    my $intf = $_->[0];
  
    # supported interfaces must have an interrupt endpoint and may have 2 bulk endpoints
    if (($intf->bNumEndpoints == 3 || $intf->bNumEndpoints == 1) &&
	($intf->endpoints->[0]->bmAttributes == 3)) {  # USB_ENDPOINT_XFER_INT
	warn "Candidate: ifnum=", $intf->bInterfaceNumber,"\n";
	&do_qmi($dev, $intf);
    } else {
	warn "Unsupported endpoint configuration on ifnum=", $intf->bInterfaceNumber,"\n";
    }
}


sub do_qmi {
    my ($dev, $intf) = @_;

    my $ifnum = $intf->bInterfaceNumber;
    if ($dev->claim_interface($ifnum) < 0) {
	warn "claim_interface failed for if $ifnum - need to unbind first?\n";
	return;
    }

    # QMI_CTL_MESSAGE_GET_VERSION_INFO
    my $qmi = pack("C*", map { hex } qw!01 0b 00 00 00 00 00 08 21 00 00 00!);
    &send_msg($dev, $ifnum, $qmi, length($qmi));

    # may have to skip a few unsolicted messages
    for (my $i = 0; $i < 10; $i++) {
	my ($len, $msg) = &recv_msg($dev, $ifnum);
	last if ($len < 0); # no need to repeat if complete failure
	last if ($len && &is_ver($msg, $len));
	# just give the device "enough time"...
	sleep(.5);
    }
    $dev->release_interface($ifnum);
}

#map { warn "num: ", $_->bInterfaceNumber(), "eps: ", $_->nNumEndpoints(), "\n" } @{$cfg->interfaces()};

# test if msg is a reply to a QMI_CTL_MESSAGE_GET_VERSION_INFO and print details if it is
sub is_ver {
    my ($msg, $len) = @_;

    warn sprintf "%02x " x length($msg) . "\n", unpack("C*", $msg) if $opt{'debug'};

    my $ret = {};
    @$ret{'tf','len','ctrl','sys','cid','flags','tid','msgid','tlvlen'} = unpack("CvCCCCCvv", $msg);

    # sanity check: tf is always one, sys must be QMI_CTL
    return undef unless ($ret->{tf} == 1 && $ret->{sys} == 0);

    # only interested in QMI_CTL_MESSAGE_GET_VERSION_INFO
    return undef unless ($ret->{'msgid'} == 0x0021);

    # add the tlv(s)
    my $tlvlen = $ret->{'tlvlen'};
    my $tlvs = substr($msg, 12);
    while ($tlvlen > 0) {
	my ($tlv, $len) = unpack("Cv", $tlvs);
	$ret->{'tlvs'}{$tlv} = substr($tlvs, 3, $len);
	$tlvlen -= $len + 3;
	$tlvs = substr($tlvs, $len + 3);
    }

    # success only if TLV 0x02 is 0 and TLV 0x01 exists
    return undef unless ((unpack("V", $ret->{'tlvs'}{0x02}) == 0) && exists($ret->{'tlvs'}{0x01}));

    # decode the list of supported systems in TLV 0x01
    my $data = $ret->{'tlvs'}{0x01};
    my $n = unpack("C", $data);
    $data = substr($data, 1);
    print "supports $n QMI subsystems:\n";
    for (my $i = 0; $i < $n; $i++) {
	my ($sys, $maj, $min) = unpack("Cvv", $data);
	my $system = $sysname{$sys} || sprintf("%#04x", $sys);
	print "  $system ($maj.$min)\n";
	$data = substr($data, 5);
    }

    return 1;
}


# send CDC encapsulated message to interface
sub send_msg {
    my ($dev, $ifnum, $msg, $len) = @_;
    my $ret = $dev->control_msg(0x21,   # USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE
				0,      # CDC SEND_ENCAPSULATED_COMMAND
				0,      # zero
				$ifnum, # wIndex = interface
				$msg,
				$len,
				1000);  # timeout in ms

    warn "control_msg() returned $ret\n" if $opt{'debug'};
    if ($ret != $len) {
	warn "Failed to send message - control_msg() returned $ret\n";
    }
    return $ret;
}

# recv CDC encapsulated message to interface
sub recv_msg {
    my ($dev, $ifnum) = @_;
    my $buf = 0 x 512; # pre-allocate buffer - libusb is not perl!
    my $ret = $dev->control_msg(0xa1,   # USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE
				1,      # CDC GET_ENCAPSULATED_RESPONSE
				0,      # zero
				$ifnum, # wIndex = interface
				$buf,
				512,
				1000);  # timeout in ms
    warn "control_msg() returned $ret\n" if $opt{'debug'};
    return ($ret, $buf);
}


sub usage {
    warn <<EOT
      Usage:
	$0 [--verbose] [--debug] --vid=<idVendor> --pid=<idProduct> [--if=<bInterfaceNumber>]

      Example:
	$0 --vid=1199 --pid=68a2

 Debugging: off
 Device: 1199:68a2
 Unsupported endpoint configuration on ifnum=0
 Unsupported endpoint configuration on ifnum=2
 Candidate: ifnum=3
 claim_interface failed for if 3 - need to unbind first?
 Candidate: ifnum=8
 supports 14 QMI subsystems:
   QMI_CTL (1.5)
   QMI_WDS (1.12)
   QMI_DMS (1.6)
   QMI_NAS (1.8)
   QMI_QOS (1.3)
   QMI_WMS (1.3)
   QMI_PDS (1.10)
   QMI_AUTH (1.1)
   QMI_VOICE (2.1)
   QMI_CAT (2.0)
   QMI UIM (1.4)
   QMI PBM (1.4)
   QMI_SAR (1.0)
   QMI_RMS (1.0)


      Note:
	It is necessary to unbind any driver from the interface(s)
	before testing.  This can be done by unloading the driver
	or writing to the driver's "unbind" file:

         # echo 2-4:1.8 > /sys/bus/usb/devices/2-4:1.8/driver/unbind


EOT
;
    exit 0;
}

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux