Jak zapewnić takie same nazwy dla portów szeregowych
Ostatnia zmiana: 2016-04-25 07:58

Problem

Posiadając podłączone do routera dwa różne urządzenia będące interfejsami szeregowymi, np. dwa modemy, nie mamy pewności który zostanie wykryty jako pierwszy i tym samym jakie będą nazwy interfejsów szeregowych. Raz zostanie wykryty pierwszy i zostają mu przypisane interfejsy powiedzmy od /dev/ttyUSB0 do /dev/ttyUSB2, drugiemu zaś następne, czyli /dev/ttyUSB3 do /dev/ttyUSB6. Innym razem jak podłączony ten drugi to może on dostać /dev/ttyUSB0 do /dev/ttyUSB3 co już powoduje problemy z konfiguracją. Ma to znaczenie jeżeli korzystamy z różnych operatorów, bo trzeba zrobić dla nich inne konfiguracje.

Jednym z rozwiązań jest na stałe przypisane określonych nazw do określonych urządzeń. Ponieważ nie da się tego prosto zrobić na poziomie sterownika (on numeruje interfejsy po prostu jako kolejne dostępne), to można to rozwiązać robiąc odpowiednie linki symboliczne.

Mamy dwa modemy: Huawei E156G oraz Huawei E3131. Ten pierwszy ma dwa interfejsy szeregowy, drugi zaś cztery. W obu są karty SIM różnych operatorów, więc istotnie jest do którego interfejsu się odwołujemy.

Przykładowe rozwiązanie

Zamiast podawać w konfiguracji /dev/ttyUSB0 chcemy podać np. /dev/modem_e3131_0. Oczywiście cały problem spada na utworzenie takiej nazwy. Do tego celu można posłużyć się zachodzącymi w systemie zdarzeniami hotpluga.

Do testów należy w katalogu /etc/hotplug.d/usb utworzyć plik o nazwie 10-serial, o następującej zawartości:


    #!/bin/sh
    echo "----" > /tmp/plik.txt
    env >> /tmp/plik.txt

Jeżeli teraz podłączymy modem, w pliku /tmp/plik.txt pojawi się kilka zmiennych przekazywanych do skryptu w momencie wystąpienia zdarzenia. Z tych ciekawych to PRODUCT - identyfikator usb urządzenia, ACTION - typ zdarzenia, DEVTYPE i DEVPATH - rodzaj urządzenia i ścieżka do niego. Po małej analizie tych danych i zebraniu wymaganych parametrów można napisać prosty skrypt, który w/w nazwy tworzy. Przykładowo może on wyglądać następująco (do wpisania w /etc/hotplug.d/usb/10-serial):


    #!/bin/sh
    if [ "$DEVTYPE" = "usb_interface" ] && [ "$ACTION" = "add" ]; then
        for tty in /sys/$DEVPATH/ttyUSB*; do
            [ -d "$tty" ] || continue
            OLDD=${tty##*/}

            # to jest E156G
            if [ "x$PRODUCT" = "x12d1/1003/0" ]; then
                NEWD="modem_e156g_"${DEVPATH##*.}
                rm /dev/$NEWD
                ln -s /dev/$OLDD /dev/$NEWD
            fi
    
            # to jest E3131
            if [ "x$PRODUCT" = "x12d1/1506/102" ]; then
                NEWD="modem_e3131_"${DEVPATH##*.}
                rm /dev/$NEWD
                ln -s /dev/$OLDD /dev/$NEWD
            fi

        done
    fi

Co daje taki skrypt? Jeżeli teraz podłączymy E156 jako pierwszy, zostaną przez system utworzone dwa interfejsy, /dev/ttyUSB0 oraz /dev/ttyUSB1, a skrypt zrobi linki:

  • /dev/modem_e156g_0 jako link do /dev/ttyUSB0
  • /dev/modem_e156g_1 jako link do /dev/ttyUSB1
Jeżeli następnie podłączymy E3131 to system utworzy cztery kolejne interfejsy: /dev/ttyUSB2, /dev/ttyUSB3, /dev/ttyUSB4 i /dev/ttyUSB5, natomiast skrypt zrobi linki:

  • /dev/modem_e3131_0 do /dev/ttyUSB2
  • /dev/modem_e3131_1 do /dev/ttyUSB3
  • /dev/modem_e3131_2 do /dev/ttyUSB4
  • /dev/modem_e313132 do /dev/ttyUSB5
Jeżeli podłączymy w odwrotnej kolejności: najpierw E3131 to system zrobi /dev/ttyUSB0, /dev/ttyUSB1, /dev/ttyUSB2 i /dev/ttyUSB3, skrypt zaś odpowiednio /dev/modem_e3131_0, /dev/modem_e3131_1, /dev/modem_e3131_2 i /dev/modem_e3131_3, a później E156G to system zrobi /dev/ttyUSB4, /dev/ttyUSB5, skrypt odpowiednio /dev/modem_e156g_0 i /dev/modem_e156g_1. Można więc w konfiguracji podać /dev/modem_e3131_0 mając pewność, że zawsze odwołamy się do tego samego urządzenia.

Problem się komplikuje, jeżeli urządzenia są takie same (a dokładniej - mają taki sam identyfikator USB - VID/PID). Wtedy zamiast bazować na zmiennej PRODUCT trzeba inaczej sprawdzić unikalność - np. odczytując numer IMEI karty SIM lub bazując na numerze portu USB do którego połączamy urządzenie.

Zamiast tworzyć linki do portów można po prostu od razu ustawiać odpowiednio konfigurację, np.


    #!/bin/sh
    if [ "$DEVTYPE" = "usb_interface" ] && [ "$ACTION" = "add" ]; then
        for tty in /sys/$DEVPATH/ttyUSB*; do
            [ -d "$tty" ] || continue
            OLDD=${tty##*/}

            # to jest E3131
            if [ "x$PRODUCT" = "x12d1/1506/102" ]; then
                NEWD=${DEVPATH##*.}
                if [ $NEWD = "0" ]; then
                    uci set network.wan.device="/dev/"$OLDD
                    uci commit network
                fi
            fi
        done
    fi

Wtedy w konfiguracji zostanie ustawiony pierwszy interfejs modemu E3131, niezależnie od tego ile jest portów szeregowych różnych urządzeń i w jakiej kolejności zostanie wykryte.