]> git.ipfire.org Git - people/stevee/network.git/commitdiff
Handle hotplugging of serial devices.
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 19 May 2013 08:56:32 +0000 (10:56 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 19 May 2013 09:08:47 +0000 (11:08 +0200)
functions.modem
functions.serial
functions.usb [new file with mode: 0644]
udev/network-hotplug-serial [new file with mode: 0755]
udev/rules.d/60-net.rules

index 1960ee64c8406bc8232df0a69b61511f8ff6b955..b23d68b5854a83348c827c908facf22ced07cf12 100644 (file)
@@ -126,6 +126,16 @@ function __modem_chat_process_output() {
        done
 }
 
+function modem_initialize() {
+       local device="${1}"
+       assert isset device
+
+       log INFO "Initializing modem ${device}"
+
+       # Reset the modem.
+       modem_chat "${device}" "${AT_INITIALIZE}"
+}
+
 # Exit codes of the sim_status function.
 EXIT_SIM_READY=0
 EXIT_SIM_PIN=1
@@ -206,6 +216,40 @@ function modem_sim_unlock() {
        return ${ret}
 }
 
+function modem_sim_auto_unlock() {
+       local device="${1}"
+       assert isset device
+
+       local pin="${2}"
+       assert isset pin
+
+       # Get the current state the SIM card is in.
+       modem_sim_status "${device}" &>/dev/null
+       local sim_status_code=$?
+
+       case "${sim_status_code}" in
+               ${EXIT_SIM_READY})
+                       # Everything's fine. The SIM card is
+                       # already unlocked.
+                       return ${EXIT_OK}
+                       ;;
+               ${EXIT_SIM_PIN})
+                       # Try to unlock the device.
+                       if modem_sim_unlock ${device} ${pin}; then
+                               return ${EXIT_OK}
+                       else
+                               return ${EXIT_ERROR}
+                       fi
+                       ;;
+               ${EXIT_SIM_PUK})
+                       log ERROR "SIM card is PUK locked. Please unlock manually."
+                       return ${EXIT_ERROR}
+                       ;;
+       esac
+
+       return ${EXIT_ERROR}
+}
+
 # Returns the vendor of the modem.
 # For example: "huawei"
 function modem_get_manufacturer() {
index 8540e2935b2ce3399efcd58116f2366df24a62be..aaa3e4eac2b347ed382b79f223e125a867574285 100644 (file)
@@ -67,3 +67,18 @@ function serial_is_modem() {
                        ;;
        esac
 }
+
+function serial_get_bus_type() {
+       local device="${1}"
+       assert isset device
+
+       case "${device}" in
+               /dev/ttyUSB*)
+                       print "usb"
+                       return ${EXIT_OK}
+                       ;;
+       esac
+
+       print "unknown"
+       return ${EXIT_ERROR}
+}
diff --git a/functions.usb b/functions.usb
new file mode 100644 (file)
index 0000000..2c6cc00
--- /dev/null
@@ -0,0 +1,70 @@
+#!/bin/bash
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2013  IPFire Network Development Team                         #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+function usb_device_find_by_tty() {
+       local tty="${1}"
+       assert isset tty
+
+       # Strip /dev.
+       tty="$(basename "${tty}")"
+
+       local path="/sys/bus/usb-serial/devices/${tty}"
+
+       # Resolve symlink
+       path="$(readlink -m "${path}")"
+
+       print "$(dirname "${path}")"
+       return ${EXIT_OK}
+}
+
+function usb_device_list_interfaces() {
+       local path="${1}"
+       assert [ -d "${path}" ]
+
+       local interface
+       for interface in ${path}/ep_*; do
+               [ -d "${interface}" ] || continue
+               print "${interface}"
+       done
+
+       return ${EXIT_OK}
+}
+
+function usb_device_get_interface_type() {
+       local interface="${1}"
+       assert isset interface
+
+       fread "${interface}/type"
+}
+
+function usb_device_has_interface_type_interrupt() {
+       local device="${1}"
+       assert isset device
+
+       local interface type
+       while read -r interface; do
+               type="$(usb_device_get_interface_type "${interface}")"
+
+               [ "${type}" = "Interrupt" ] && return ${EXIT_TRUE}
+       done <<< "$(usb_device_list_interfaces "${device}")"
+
+       return ${EXIT_FALSE}
+}
diff --git a/udev/network-hotplug-serial b/udev/network-hotplug-serial
new file mode 100755 (executable)
index 0000000..ead51af
--- /dev/null
@@ -0,0 +1,120 @@
+#!/bin/bash
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2011  Michael Tremer & Christian Schmidt                      #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+. /usr/lib/network/functions
+
+# Setup logging.
+LOG_FACILITY="network-hotplug-serial"
+
+# Associated hooks.
+ASSOCIATED_HOOKS="modem"
+
+# Read network configuration.
+network_config_read
+
+log DEBUG "Called with ACTION='${ACTION}', DEVNAME='${DEVNAME}'."
+
+# If DEVNAME is not set, we cannot handle anything here and will
+# exit silently.
+isset DEVNAME || exit ${EXIT_OK}
+
+# Check if the udev environment variables are properly set.
+assert isset ACTION
+
+case "${ACTION}" in
+       add|register)
+               # Check if the device node already exists.
+               if ! serial_exists ${DEVNAME}; then
+                       log DEBUG "Device node ${DEVNAME} does not exist"
+                       exit ${EXIT_ERROR}
+               fi
+
+               # USB modems often register multiple serial interfaces.
+               # Some of them allow to send AT commands, some don't know.
+               # However, there is only one interface that can actually be used
+               # to connect to somewhere. This interface always runs in
+               # interrupt mode. Exit for all interfaces, not in this mode.
+               bus_type="$(serial_get_bus_type "${DEVNAME}")"
+               if [ "${bus_type}" = "usb" ]; then
+                       # Find USB device.
+                       usb_device="$(usb_device_find_by_tty "${DEVNAME}")"
+
+                       # Find an interface in interrupt mode.
+                       if ! usb_device_has_interface_type_interrupt "${usb_device}"; then
+                               log DEBUG "USB device has no interface in interrupt mode: ${DEVNAME}"
+                               exit ${EXIT_OK}
+                       fi
+               fi
+
+               # Check if the device is actually a modem.
+               if ! serial_is_modem ${DEVNAME}; then
+                       log DEBUG "${DEVNAME} does not look like a modem"
+                       exit ${EXIT_OK}
+               fi
+
+               # When we get here, the modem is alive and responds to
+               # AT commands.
+               log DEBUG "${DEVNAME} looks like a modem"
+
+               # Initialize the modem here. Resets all established connections
+               # and so on.
+               modem_initialize "${DEVNAME}"
+
+               # Unlock the SIM card if it has one.
+               if modem_sim_locked "${DEVNAME}"; then
+                       log ERROR "SIM card is locked. Needs unlocking."
+                       exit ${EXIT_OK}
+               fi
+
+               # Try to find the zone configuration by the IMSI of the
+               # SIM card.
+               sim_imsi="$(modem_get_sim_imsi "${DEVNAME}")"
+               isset sim_imsi || exit ${EXIT_OK}
+
+               for zone in $(zones_get_all); do
+                       # XXX Check if the zone is enabled.
+
+                       # Skip unsupported hook types.
+                       hook="$(zone_get_hook "${zone}")"
+                       list_match "${hook}" ${ASSOCIATED_HOOKS} || continue
+
+                       # Read IMSI from zone configuration.
+                       zone_imsi="$(zone_config_option "${zone}" IMSI)"
+
+                       # If IMSIs match, we start that zone.
+                       if [ "${zone_imsi}" = "${sim_imsi}" ]; then
+                               # Start the matching zone.
+                               zone_up "${zone}"
+                               break
+                       fi
+               done
+
+               exit ${EXIT_OK}
+               ;;
+
+       remove|unregister)
+               # After the interface has been removed/unplugged,
+               # there are often daemons (like pppd) which need
+               # to be stopped.
+               ;;
+esac
+
+exit ${EXIT_OK}
index ab1779b34800e563bfc9d9993f459023e9a7e752..e5d24b9beb184c753932a4ade5174be0073492ab 100644 (file)
@@ -6,3 +6,6 @@ ACTION=="add", SUBSYSTEM=="net", PROGRAM="/usr/lib/udev/network-hotplug-rename",
 
 # Handle all plugged-in devices.
 SUBSYSTEM=="net", RUN+="/usr/lib/udev/network-hotplug"
+
+# Handle all serial devices (like modems and so on...).
+KERNEL=="ttyUSB*", RUN+="/usr/lib/udev/network-hotplug-serial"