I’m making a Raspberry Pi to put at a “remote” location, and I needed network connectivity. I had an old USB 3G dongle lying around. It appears to be a Vodafone-branded Huawei K4302 modem, supporting HSPA+ (no LTE).

Picture of the front of the USB stick.
Picture of the back of the USB stick.

The modem presents itself on the USB bus as follows:

Bus 001 Device 005: ID 12d1:1f1c Huawei Technologies Co., Ltd.
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol       255
  bMaxPacketSize0        64
  idVendor           0x12d1 Huawei Technologies Co., Ltd.
  idProduct          0x1f1c
  bcdDevice            1.02
  iManufacturer           1 Vodafone(Huawei)
  iProduct                2 HUAWEI Mobile
  iSerial                 3 FFFFFFFFFFFFFFFF
  bNumConfigurations      2
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           32
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower              500mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         8 Mass Storage
      bInterfaceSubClass      6 SCSI
      bInterfaceProtocol     80 Bulk-Only
      iInterface              4 Mass Storage
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           87
    bNumInterfaces          2
    bConfigurationValue     2
    iConfiguration          0
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower              500mA
    Interface Association:
      bLength                 8
      bDescriptorType        11
      bFirstInterface         0
      bInterfaceCount         2
      bFunctionClass          2 Communications
      bFunctionSubClass      14
      bFunctionProtocol       0
      iFunction               8 K4203
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         2 Communications
      bInterfaceSubClass     14
      bInterfaceProtocol      0
      iInterface              5 CDC Network Control Model (NCM)
      CDC Header:
        bcdCDC               1.10
      CDC MBIM:
        bcdMBIMVersion       1.00
        wMaxControlMessage   1024
        bNumberFilters       16
        bMaxFilterSize       20
        wMaxSegmentSize      1500
        bmNetworkCapabilities 0x20
          8-byte ntb input size
      CDC Union:
        bMasterInterface        0
        bSlaveInterface         1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0010  1x 16 bytes
        bInterval               9
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass        10 CDC Data
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      2
      iInterface              6 CDC Network Data
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       1
      bNumEndpoints           2
      bInterfaceClass        10 CDC Data
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      2
      iInterface              6 CDC Network Data
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
Device Qualifier (for other device speed):
  bLength                10
  bDescriptorType         6
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol       255
  bMaxPacketSize0        64
  bNumConfigurations      2
Device Status:     0x0001
  Self Powered

Its a fairly typical “flip-flop” device that starts out by pretending to be a storage device (CD-ROM is this case), but can be switched to the actual modem. Under linux, this switching is done by usb_modeswitch

USB reset

Normally, this all goes automatically. But it didn’t.

Every time usb_modeswitch tried to switch the dongle, I got:

usb usb1-port3: disabled by hub (EMI?), re-enabling...

Followed by a complete reset of everything attached to the USB-bus (including wired ethernet). The message (and the search results about the message) seem to be unrelated to mode switching. I don’t think it’s a power issue. The Raspberry Pi is capable to provide 1.2A of power to its USB ports. Since the 3G modem is the only thing plugged in, and a single USB port should not require more than 500mA, I should have 700mA headroom.

The stick is plugged directly into the port, without any extension cord that could pick up EMI, but I tried adding a cord anyway, but got the same behaviour.

But I found it peculiar that the bus reset happens right at the time of the mode-switch, so I looked into that.

Alternative switching method

I found a page describing another switching method, that I automated by adding the following line in /etc/udev/rules.d/99-huawei-modem.rules:

ACTION=="add", ATTR{idVendor}=="12d1", ATTR{idProduct}=="1f1c", RUN+="usb_modeswitch -v 12d1 -p 1f1c -W -I -M 55534243123456780000000000000011062000000101000100000000000000"

This worked every time without resetting the USB bus…

However, while trying to reproduce these issues while writing this post, it just worked every time, even with the normal switching logic…:


USB_ModeSwitch log from Sun Dec 23 09:01:16 GMT 2018

Use global config file: /etc/usb_modeswitch.conf
Adjust delay for USB storage devices ...
 Delay set to 4 seconds

Raw parameters: {--switch-mode} {1-1.1.3:1.0}
Use top device dir /sys/bus/usb/devices/1-1.1.3
Check class of first interface ...
 Interface 0 class is 08.

----------------
USB values from sysfs:
  manufacturer	Vodafone(Huawei)
  product	HUAWEI Mobile
  serial	FFFFFFFFFFFFFFFF
----------------
Found packed config collection /usr/share/usb_modeswitch/configPack.tar.gz
ConfigList: pack/12d1:1f1c pack/12d1:#linux
SCSI attributes not needed, move on
Check config: pack/12d1:1f1c
! matched. Read config data
Extract config 12d1:1f1c from collection /usr/share/usb_modeswitch/configPack.tar.gz
Command line:
usb_modeswitch -W -D  -b 1 -g 5 -v 12d1 -p 1f1c -f $flags(config)

Verbose debug output of usb_modeswitch and libusb follows
(Note that some USB errors are to be expected in the process)
--------------------------------

Read long config from command line

 * usb_modeswitch: handle USB devices with multiple modes
 * Version 2.5.0 (C) Josua Dietze 2017
 * Based on libusb1/libusbx

 ! PLEASE REPORT NEW CONFIGURATIONS !

DefaultVendor=  0x12d1
DefaultProduct= 0x1f1c
TargetVendor=   0x12d1
TargetProductList="157a,1590"
HuaweiNewMode=1
System integration mode enabled

Use given bus/device number: 001/005 ...
Look for default devices ...
 bus/device number matched
  found USB ID 12d1:1f1c
   vendor ID matched
   product ID matched
 Found devices in default mode (1)
Get the current device configuration ...
Current configuration number is 1
Use interface number 0
 with class 8
Use endpoints 0x01 (out) and 0x81 (in)

USB description data (for identification)
-------------------------
Manufacturer: Vodafone(Huawei)
     Product: HUAWEI Mobile
  Serial No.: FFFFFFFFFFFFFFFF
-------------------------
Using standard Huawei switching message
Looking for active driver ...
 OK, driver detached
Set up interface 0
Use endpoint 0x01 for message sending ...
Trying to send message 1 to endpoint 0x01 ...
 OK, message successfully sent
Read the response to message 1 (CSW) ...
 Response successfully read (13 bytes), status 0
Reset response endpoint 0x81
Reset message endpoint 0x01
ok:busdev
--------------------------------
(end of usb_modeswitch output)

Check success of mode switch for max. 20 seconds ...
 Wait for device file system (1 sec.) ...
 Read attributes ...
 All attributes matched
Mode switching was successful, found 12d1:1590 (Vodafone(Huawei): HUAWEI Mobile)
Logger is /usr/bin/logger
Check for AVOID_RESET_QUIRK kernel attribute
 AVOID_RESET_QUIRK activated

All done, exit

Dialing out

Once switched, the modem presents itself as a network interface using the cdc_ether driver, including a DHCP-server. So this usually yields a fully functional network connection, fully automatically.

My SIM-card was PIN-locked. In that case, the modem redirects all HTTP-requests to its web-based interface, and asks for the PIN.

I wanted to automate this step. Which seems to be a bit more complicated than expected. I sniffed a promising call to /api/pin/operate with an XML-body:

01234302258

But I couldn’t reproduces this using Curl. Apparently, the token attribute is not constant. So where does that come from? Luckily, the JavaScript used in the web-interface is not obscured, so it’s fairly easy to find out that it’s a variable set in /html/js/vendor.js, and is different each time. These steps seem to work:

PIN="1234"

TOKEN="$( curl --silent --connect-timeout 5 http://192.168.9.1/html/js/vendor.js | grep 'var STR_AJAX_VALUE' | sed 's/.*"\([0-9]*\)".*/\1/' )"

curl -v http://192.168.9.1/api/pin/operate -d "0${PIN}${TOKEN}"