USB Serial console server

If you ever ran a serial console server with a lot of USB dongles you'll find that the numbering of your devices may change due to un-re-plug events or when you reboot the machine where your USB adapters are attached to.

Please see the answers to my post from Poul-Henning Kamp and Daniel O'Connor. Also there is a review open D21886 highly related to this.

I have since (in a night hack; forgive the horrible shell) improved the script as I also like to have device names and not just serial numbers. In addition I have 4-port USB-Serial dongles which means I see the same serial number 4 times.

attach 500 {
        #match "system"         "USB";
        #match "subsystem"      "DEVICE";
        match "device-name"     "(uftdi|umodem|uplcom|uslcom)[0-9]*";
        match "vendor"          ".+";
        match "product"         ".+";
        match "sernum"          ".+";
        action "/usr/local/libexec/usbserial ATTACH $sernum $ttyname $device-name";
        #action "ln -fs /dev/cua$ttyname /dev/cua_$sernum";
};

notify 500 {
        match "system"          "USB";
        match "subsystem"       "DEVICE";
        match "type"            "DETACH";
        match "vendor"          ".+";
        match "product"         ".+";
        match "sernum"          ".+";
        action "/usr/local/libexec/usbserial $type $sernum $ttyname $device-name";
        #action "rm -f /dev/cua_$sernum";
};

 #!/bin/sh

MODE="$1"
SERNUM="$2"
TTYNAME="$3"
DEVNAME="$4"

if test -z "${MODE}" -o -z "${SERNUM}"; then
        logger -p daemon.err -t usbserial "$0 mandatory argument empty: '$1' '$2'"
        printf "$0 ERROR: mandatory argument empty: '$1' '$2'\n" >&2
        exit 1
fi

# Prefixes for tty and cua, for serial and name.
CUA_S_PREFIX="/dev/cua.s."
CUA_N_PREFIX="/dev/cua.n."
TTY_S_PREFIX="/dev/tty.s."
TTY_N_PREFIX="/dev/tty.n."

S="${SERNUM}"
NN="${SERNUM}"
DETACHNODES=""
case "${SERNUM}" in
XXXXXXXX)  NN="nanopc-t4.1500000" ;;
FTYYYYY)
        FTYYYYY_S_0="${S}.0"
        FTYYYYY_S_1="${S}.1"
        FTYYYYY_S_2="${S}.2"
        FTYYYYY_S_3="${S}.3"
        FTYYYYY_N_0="amachine.9600"
        FTYYYYY_N_1="bsd42.115200"
        FTYYYYY_N_2="ccomp.19200"
        FTYYYYY_N_3="dlang.115200"
        
        case "${MODE}" in
        ATTACH)
                # 4 Ports; needs special treatment; 4x called on attach (assumed in order), 1x on detach
                if test -e ${CUA_S_PREFIX}${FTYYYYY_S_3}; then
                        logger -p daemon.err -t usbserial "$0 4 port serial ${SERNUM} with more device nodes"
                        printf "$0 ERROR: 4 port serial ${SERNUM} with more device nodes\n" >&2
                        exit 2
                elif test -e ${CUA_S_PREFIX}${FTYYYYY_S_2}; then
                        S="${FTYYYYY_S_3}"
                        NN="${FTYYYYY_N_3}"
                elif test -e ${CUA_S_PREFIX}${FTYYYYY_S_1}; then
                        S="${FTYYYYY_S_2}"
                        NN="${FTYYYYY_N_2}"
                elif test -e ${CUA_S_PREFIX}${FTYYYYY_S_0}; then
                        S="${FTYYYYY_S_1}"
                        NN="${FTYYYYY_N_1}"
                else
                        S="${FTYYYYY_S_0}"
                        NN="${FTYYYYY_N_0}"
                fi
                ;;
        DETACH)
                DETACHNODES="${DETACHNODES} ${FTYYYYY_S_0}:${FTYYYYY_N_0}"
                DETACHNODES="${DETACHNODES} ${FTYYYYY_S_1}:${FTYYYYY_N_1}"
                DETACHNODES="${DETACHNODES} ${FTYYYYY_S_2}:${FTYYYYY_N_2}"
                DETACHNODES="${DETACHNODES} ${FTYYYYY_S_3}:${FTYYYYY_N_3}"
                ;;
        esac
        ;;
*)
        logger -p daemon.info -t usbserial "$0 unknown serial device '${SERNUM}'"
        printf "$0 INFO: unknown serial device '${SERNUM}'\n" >&2
        ;;
esac

case "${MODE}" in
ATTACH)
        if test -z "${TTYNAME}"; then
                logger -p daemon.err -t usbserial "$0 no ttyname given for '${SERNUM}'"
                printf "$0 ERROR: no ttyname given for '${SERNUM}'\n" >&2
                exit 1
        fi
        logger -p daemon.notice -t usbserial "$0 creating symlinks for ${TTYNAME} -> ${S}"
        ln -sf cua${TTYNAME} ${CUA_S_PREFIX}${S}
        ln -sf tty${TTYNAME} ${TTY_S_PREFIX}${S}
        case "${NN}" in
        ${S})   ;;
        *)
                logger -p daemon.notice -t usbserial "$0 creating symlinks for ${TTYNAME} -> ${NN}"
                ln -sf cua${TTYNAME} ${CUA_N_PREFIX}${NN}
                ln -sf tty${TTYNAME} ${TTY_N_PREFIX}${NN}
                ;;
        esac
        ;;
DETACH)
        # Strip any leading space.
        DETACHNODES=${DETACHNODES#* }
        case "${DETACHNODES}" in
        "")
                if test ! -L ${CUA_S_PREFIX}${S}; then
                        logger -p daemon.notice -t usbserial "$0 SKIPPING removing symlinks for ${CUA_S_PREFIX}${S}: `ls -l ${CUA_S_PREFIX}${S}`"
                        exit 0
                fi
                logger -p daemon.notice -t usbserial "$0 removing symlinks for ${SERNUM} -> ${S} ${NN}"
                rm -f ${CUA_S_PREFIX}${S} ${TTY_S_PREFIX}${S} \
                        ${CUA_N_PREFIX}${NN} ${TTY_N_PREFIX}${NN}
                ;;
        [A-Za-z0-9]*)
                for x in ${DETACHNODES}; do
                        s=${x%%:*}
                        n=${x##*:}
                        if test ! -L ${CUA_S_PREFIX}${s}; then
                                logger -p daemon.notice -t usbserial "$0 SKIPPING removing symlinks for ${CUA_S_PREFIX}${s} / ${n}: `ls -l ${CUA_S_PREFIX}${s}  ${CUA_S_PREFIX}${n}`"
                                continue
                        fi
                        logger -p daemon.notice -t usbserial "$0 removing symlinks for ${SERNUM} -> ${s} ${n}"
                        rm -f ${CUA_S_PREFIX}${s} ${TTY_S_PREFIX}${s} \
                                ${CUA_N_PREFIX}${n} ${TTY_N_PREFIX}${n}
                done
                ;;
        *)      
                logger -p daemon.err -t usbserial "$0 unsupported detach nodes '${DETACHNODES}' for '${SERNUM}'"
                printf "$0 ERROR: unsupported number of detach nodes ${DETACHNODES} for '${SERNUM}'\n" >&2
                exit 3
                ;;
        esac
        ;;
*)
        logger -p daemon.err -t usbserial "$0 mode '${MODE}' unknown for '${SERNUM}'"
        printf "$0 ERROR: mode '${MODE}' unknown for '${SERNUM}'\n" >&2
        exit 1
        ;;
esac

# end

References

  1. https://lists.freebsd.org/pipermail/freebsd-arch/2021-April/020322.html

  2. https://lists.freebsd.org/pipermail/freebsd-arch/2021-April/020324.html

  3. https://lists.freebsd.org/pipermail/freebsd-arch/2021-April/020325.html

  4. https://reviews.freebsd.org/D21886

BjoernZeeb/USBSerialConsole (last edited 2021-05-12T20:16:38+0000 by BjoernZeeb)