Воспользуемся способностью modemmanager работать без udev для подключения к LTE сети в окружении с очень ограниченными ресурсами с помощью модема Sierra wireless em7455.

Проблема

В нормальных условиях modemmanager использует udev для идентификации и горячего подключения модемов. Udev - штука полезная, но и объёмная. Кроме того, ещё один процесс для и так не самого быстрого одноядерного процессора выглядит как отличный кандидат на устранение, тем более что такая возможность имеется.

Вариант применения modemmanager без udev хоть и является относительно маргинальным, тем не менее вполне используется в дикой природе, например в openwrt.

Строим скрипт

Краеугольным камнем этого скрипта является функция --report-kernel-event утилиты mmcli.

Перед тем как уведомлять modemmanager о событии, нужно разобраться с форматом уведомления о событии:

--report-kernel-event=['KEY1=VALUE1,KEY2=VALUE2,...']
        Manually  report  kernel events, instead of relying on udev (e.g.
        if the daemon is running with --no-auto-scan or if the system was
        built without udev support).

        The supported KEYs are:

          'action'
                 Action to report, one of 'add' or 'remove'. Required.

          'subsystem'
                 Subsystem of the  specific  port  being  reported,  e.g.
                 'tty' (for serial ports),

          'name' Name of the port being reported, e.g. 'ttyACM0', 'wwan0'
                 or 'cdc-wdm0'.

          'uid'  The  specific  UID  of  the  device,  equivalent  to the
                 ID_MM_PHYSDEV_UID udev tag. All ports reported with  the
                 same 'UID' value will be considered part of the same de‐
                 vice,  which may be useful for e.g. modems with multiple
                 platform TTYs.

Проще всего, на мой взгляд, посмотреть параметры в udev скриптах modemmanager.

Идентифицируем модем:

$ lsusb
...
1199:9071
...

По этому vid/pid находим udev скрипт:

$ cat ModemManager/src/plugins/sierra/77-mm-sierra.rules
...
ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9071", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9071", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9071", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
...

Получаем:

  • action: add
  • subsystem: tty
  • name: ttyACMx, AT команды принимаются на интерфейсе с последним интерфейсом

Таким образом, команда будет выглядеть следующим образом:

$ mmcli --report-kernel-event="action=add,subsystem=tty,name=${ttyusb}"

Где ttyusb предстоит определить. Т.к. в нашей системе другого модема и других USB устройств пока не предвидется, нужный ttyACM всегда будет последним. Определим его следующим образом:

$ ttyusb=$(ls /dev | grep ttyUSB | tail -n 1)

TL;DR

В итоге получаем такой скрипт для подключения модема без udev:

#!/bin/bash

TIMEOUT_USBDEV=20
TIMEOUT_MM=10
TIMEOUT_MMMODEM=20
TIMEOUT_OPID=10
VIDPID_SIERRA_EM7455="1199:9071"

/etc/init.d/S44modem-manager restart

# Check if m.2 modem is present

ctr=0
while :; do
    sleep 1
    echo "Waiting for USB device"
    if [ -n "$(lsusb | grep ${VIDPID_SIERRA_EM7455})" ]; then
        echo "Found Sierra EM7455"
        break;
    fi
    if [ ${ctr} -gt ${TIMEOUT_USBDEV} ]; then
        echo "Error: Timeout while waiting for USB device: $(lsusb)"
        exit 1
    fi
    ctr=$((++ctr))
done

ttyusb=$(ls /dev | grep ttyUSB | tail -n 1)
if [ -z ${ttyusb} ]; then
	echo "Error: tty not found"
	exit 2
fi

echo "Wait for ModemManager"
ctr=0
while [ $(ps ax | grep ModemManager | wc -l) -ne 2 ]; do
    sleep 1
    if [ ${ctr} -gt ${TIMEOUT_MM} ]; then
        echo "Error: Timeout while waiting for ModemManager"
        exit 3
    fi
    ctr=$((++ctr))
done
echo "Report tty modem ${ttyusb} to mm"
mmcli --report-kernel-event="action=add,subsystem=tty,name=${ttyusb}"
sleep 1

ctr=0
while [ "$(mmcli -L)" = "No modems were found" ]; do
    sleep 1;
    echo "retry: $(mmcli -L)";
    if [ ${ctr} -gt ${TIMEOUT_MMMODEM} ]; then
        echo "Error: Timeout while waiting for modem"
        exit 4
    fi
    ctr=$((++ctr))
done

NR=$(mmcli -L | sed -e "s|^.*/Modem/\([0-9]\) .*$|\1|")
echo "Got modem id: ${NR}"

echo "Check SIM presence"
sim=$(mmcli -m ${NR} | grep SIM | wc -l)
if [ ${sim} -eq 0 ]; then
    echo "Error: SIM is missing: $(mmcli -m ${NR})"
    exit 5
fi

После успешного завершения этого скрипта modemmanager должен быть уведомлен о наличии модема, и должен находиться в состоянии готовом к подключению к сети.

В следующий раз поговорим о том, что делать дальше.