Воспользуемся способностью 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 должен быть уведомлен о наличии модема, и должен находиться в состоянии готовом к подключению к сети.
В следующий раз поговорим о том, что делать дальше.