0%

usb-modeswitch 使用及代码分析

usb modeswitch 是一种模式切换工具,用于控制具有 多种模式 的 USB 设备。

I

无线网卡首次插入电脑时,它们会被识别为一个闪存设备,然后开始安装存储于其中的驱动程序。在安装完驱动程序之后就会在内部切换 USB 设备的连接模式,存储设备会消失,然后会出现一个新的设备。

目前许多这种设备都可以在 Linux 的驱动下工作,如 usb-storage (存储设备的驱动模块)和 options (高速 Modem 的驱动模块),接下来的事情就是如何从存储设备到 Modem 的切换。

依赖于 libusb 库, usb modeswitch 可以从一个配置文件中获取重要的参数,然后完成全部的初始化和通信工作,这样便使得用户可以轻松地处理这个过程。

源码与安装

标准自动化用法

通过 udev 事件和规则,标准自动化用法详见 usb_modeswitch 使用详解

内核相关问题

另一种能够影响内核行为的方法是“usb-storage”的“delay_use”参数,这个参数会设置存储设备插入系统到实际使用(可能会自动挂载)之间的延迟时间,以秒为单位。默认值为 5,这个参数可能会影响某些条件下的切换结果

嵌入式移植

  • 建立 usb-storage - options 对应表,具体对应关系从 usb modeswitch.d 中获得,例如文件 0421:062c
# Nokia CS-19
TargetVendor=0x0421
TargetProductList="062d,062f"
StandardEject=1

获得对应关系 0421:062c - 0421:062d 0421:062f

  • 切换设备
    usb_modeswitch -c /etc/usb_modeswitch.d/0421:062c -v 0x0421 -p 0x062c &

此时能从终端看到设备 拔出插入 的信息,示例如下:

[10559.330000] usb 1-1: new high speed USB device using EHCI-NationalChip and address 6
[10559.500000] usb 1-1: configuration #1 chosen from 1 choice
[10559.510000] scsi11 : SCSI emulation for USB Mass Storage devices
[10559.580000] scsi12 : SCSI emulation for USB Mass Storage devices
[Hotplug] dev: 1-1
[Hotplug] action: add
[Hotplug] dev: 1-1:1.0
[Hotplug] action: add
[Hotplug]: usb id stack
1-1+12d1:1446
[Hotplug] dev: 1-1:1.1
[Hotplug] action: add

Look for target devices ...
 No devices in target mode or class found
Look for default devices ...
 Found devices in default mode (1)
Access device 006 on bus 001
Get the current device configuration ...
Current configuration number is 1
Use interface number 0
 with class 8
Use endpoints 0x01 (out) and 0x81 (in)
Using standard Huawei switching message
Looking for active drivers ...
 OK, driver detached
 OK, driver detached
Set up int[10560.320000] usb 1-1: USB disconnect, address 6
erface 0
Use endpoint 0x01 for message sending ...
Trying to send message 1 to endpoint 0x01 ...
 Device seems to have vanished right after sending. Good.
 Device is gone, skip any further commands
-> Run lsusb to note any changes. Bye!

[Hotplug] dev: 1-1:1.0
[Hotplug] action: remove
[Hotplug] dev: 1-1:1.1
[Hotplug] action: remove
[Hotplug] dev: 1-1
[Hotplug] action: remove
[Hotplug]: usb id stack
[10564.390000] usb 1-1: new high speed USB device using EHCI-NationalChip and address 7
[10564.550000] usb 1-1: configuration #1 chosen from 1 choice
[10564.580000] usb-storage: probe of 1-1:1.0 failed with error -5
[10564.590000] option 1-1:1.0: GSM modem (1-port) converter detected
[10564.630000] usb 1-1: GSM modem (1-port) converter now attached to ttyUSB0
[Hotplug] dev: 1-1
[Hotplug] action: add
[10564.710000] usb-storage: probe of 1-1:1.1 failed with error -5
[10564.860000] usb-storage: probe of 1-1:1.2 failed with error -5
[10564.960000] usb-storage: probe of 1-1:1.3 failed with error -5
[10564.970000] option 1-1:1.3: GSM modem (1-port) converter detected
[10565.040000] usb 1-1: GSM modem (1-port) converter now attached to ttyUSB1
[10565.080000] usb-storage: probe of 1-1:1.4 failed with error -5
[10565.090000] option 1-1:1.4: GSM modem (1-port) converter detected
[Hotplug]: usb id stack
1-1+12d1:1436
[10565.250000] usb 1-1: GSM modem (1-port) converter now attached to ttyUSB2
[Hotplug] dev: 1-1:1.0
[Hotplug] action: add
[Hotplug] dev: 1-1:1.2
[Hotplug] action: add
[Hotplug] dev: 1-1:1.1
[Hotplug] action: add
[Hotplug] dev: 1-1:1.3
[Hotplug] action: add
[Hotplug] dev: 1-1:1.5
[Hotplug] action: add
[Hotplug] dev: 1-1:1.4
[Hotplug] action: add

>>>>>>>>>>>>>>>>>> 3G Start!
TIMEOUT 5
ABORT 'NO CARRIER'
ABORT 'ERROR'
ABORT 'NO DIALTONE'
ABORT 'BUSY'
ABORT 'NO ANSWER'
'' /rAT
OK /rATZ
OK /rAT+CGDCONT=1,"IP","3gnet",,0,0
OK-AT-OK ATDT*99#
CONNECT /d/c

[10575.350000] scsi18 : SCSI emulation for USB Mass Storage devices
[Hotplug] dev: 1-1:1.6
[Hotplug] action: add
[10585.390000] scsi19 : SCSI emulation for USB Mass Storage devices
[10585.400000] scsi 18:0:0:0: CD-ROM            HUAWEI   Mass Storage     2.31 PQ: 0 ANSI: 2
timeout set to 5 seconds
abort on (NO CARRIER)
abort on (ERROR)
abort on (NO DIALTONE)
abort on (BUSY)
abort on (NO ANSWER)
send (/rAT^M)
[10585.460000] scsi 18:0:0:0: Attached scsi generic sg0 type 5
expect (OK)
AT^M^M
OK
 -- got it

上述打印可以看出基本流程

  • 插入设备,识别为 usb-storage
  • 检测到对应关系,启动 usb modeswitch
  • usb-storage 弹出
  • option 设备
  • 3G 连接流程启动

代码分析

usb modeswitch v2.5.2 为例

/* Detach driver
 */
int detachDriver()
{
    int ret;
    SHOW_PROGRESS(output,"Looking for active driver ...\n");
    ret = libusb_kernel_driver_active(devh, 0);
    if (ret == LIBUSB_ERROR_NOT_SUPPORTED) {
        fprintf(output," Can't do driver detection on this platform.\n");
        return 2;
    }
    if (ret < 0) {
        fprintf(output," Driver check failed with error %d. Try to continue\n", ret);
        return 2;
    }
    if (ret == 0) {
        SHOW_PROGRESS(output," No active driver found. Detached before or never attached\n");
        return 1;
    }

    ret = libusb_detach_kernel_driver(devh, Interface);
    if (ret == LIBUSB_ERROR_NOT_SUPPORTED) {
        fprintf(output," Can't do driver detaching on this platform.\n");
        return 2;
    }
    if (ret == 0) {
        SHOW_PROGRESS(output," OK, driver detached\n");
    } else
        SHOW_PROGRESS(output," Driver detach failed (error %d). Try to continue\n", ret);
    return 1;
}

int main(int argc, char **argv)
{
...
    /* libusb initialization */
    if ((libusbError = libusb_init(&ctx)) != LIBUSB_SUCCESS) {
        fprintf(stderr, "Error: Failed to initialize libusb. %s (%d)\n\n",
                libusb_error_name(libusbError), libusbError);
        exit(1);
    }
...

    if (dev == NULL) {
        SHOW_PROGRESS(output," No bus/device match. Is device connected? Abort\n\n");
        close_all();
        exit(0);
    } else {
        if (devnum == -1) {
            devnum = libusb_get_device_address(dev);
            busnum = libusb_get_bus_number(dev);
            SHOW_PROGRESS(output,"Access device %03d on bus %03d\n", devnum, busnum);
        }
        libusb_open(dev, &devh);
        if (devh == NULL) {
            SHOW_PROGRESS(output,"Error opening the device. Abort\n\n");
            abortExit();
        }
    }
...

    } else if (ModeMap & HUAWEINEW_MODE) {
        SHOW_PROGRESS(output,"Using standard Huawei switching message\n");
        detachDriver();
        strcpy(Messages[0],"55534243123456780000000000000011062000000101000100000000000000");
        switchSendMessage();
    }
...

    /* No "removal" check if these are set */
    if ((Configuration > 0 || AltSetting > -1) && !ResetUSB) {
        libusb_close(devh);
        devh = NULL;
    }

    if (ResetUSB) {
        resetUSB();
        devh = NULL;
    }
...
}

Ref

  1. Setting up a Huawei E3276-150 4G/LTE USB modem on Ubuntu Server/Desktop
  2. usb_modeswitch 使用详解
  3. USB_ModeSwitch - Handling Mode-Switching USB Devices on Linux
  4. USB_ModeSwitch 介绍
  5. libusb API 入门