diff --git a/general/overlay/etc/init.d/S38mdev b/general/overlay/etc/init.d/S38mdev index 47da55b519..7b92f0c55c 100755 --- a/general/overlay/etc/init.d/S38mdev +++ b/general/overlay/etc/init.d/S38mdev @@ -5,6 +5,13 @@ case "$1" in echo "Starting mdev..." echo /sbin/mdev > /proc/sys/kernel/hotplug mdev -s + # Whatever was plugged in at power-on was announced before mdev became + # the hotplug helper, and mdev -s walks only devices with a node, not + # the USB interfaces a driver binds to. Announce them again, so a phone + # already tethering at boot gets its driver like one plugged in later. + for uevent in /sys/bus/usb/devices/*:*/uevent; do + [ -w "$uevent" ] && echo add > "$uevent" + done ;; stop) diff --git a/general/overlay/etc/mdev.conf b/general/overlay/etc/mdev.conf index 3b119e307d..1eec32b70e 100644 --- a/general/overlay/etc/mdev.conf +++ b/general/overlay/etc/mdev.conf @@ -18,3 +18,11 @@ mmcblk[0-9]p[0-9] root:disk 660 *(/lib/mdev/automount.sh) sd[a-z] root:disk 660 *(/lib/mdev/automount.sh) sd[a-z][0-9] root:disk 660 *(/lib/mdev/automount.sh) + +# USB tethering: a phone, or a modem in RNDIS, ECM or NCM mode. The kernel +# builds these drivers as modules and nothing else loads them, so a phone that +# tethered gave no usb0 at all (#1112). The classes are the drivers' own; a +# blanket MODALIAS rule would also load usbserial for a modem before +# rndis_host, which /etc/wireless/modem exists to prevent. +$MODALIAS=usb:.*ic(E0isc01ip03|EFisc01ip01|02isc02ipFF|02isc06ip00|02isc0Dip00)in.* root:root 660 @modprobe -q "$MODALIAS" +$INTERFACE=usb0 root:root 660 ! *(/lib/mdev/usbnet.sh) diff --git a/general/overlay/etc/network/interfaces.d/usb0 b/general/overlay/etc/network/interfaces.d/usb0 index a84312310c..b4beba0359 100644 --- a/general/overlay/etc/network/interfaces.d/usb0 +++ b/general/overlay/etc/network/interfaces.d/usb0 @@ -1 +1,2 @@ iface usb0 inet dhcp + metric 10 diff --git a/general/overlay/lib/mdev/usbnet.sh b/general/overlay/lib/mdev/usbnet.sh new file mode 100755 index 0000000000..f06528aff1 --- /dev/null +++ b/general/overlay/lib/mdev/usbnet.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# +# usb0 comes and goes with the cable, long after S40network has run: Android +# switches tethering off whenever the cable is pulled, so the phone is +# re-plugged and re-enabled at any time. Bring the interface up when it appears +# and forget it when it goes. Without the ifdown, ifupdown still records the +# vanished interface as configured, and the next plug got "ifup: interface usb0 +# already configured" and no address. + +# Both halves under one lock: the cable can be pulled while udhcpc is still in +# its foreground retries, and an ifdown that finished first would leave the +# late ifup's state behind for the next plug to trip over. The lock orders +# nothing, though -- a pull within a second of the plug took it first -- so +# the add looks again once it holds it. +lock=/var/run/usbnet.$MDEV.lock + +case "$ACTION" in + add) + # Only the host side of a tether. A USB gadget's usb0 -- the camera + # being the device -- has no DHCP server across the cable, and + # udhcpc's leasefail would hand it the camera's ipaddr. + case "$(readlink "/sys/class/net/$MDEV/device/driver")" in + */rndis_host | */cdc_ether | */cdc_ncm) ;; + *) exit 0 ;; + esac + # In the background: mdev waits for its helper, and udhcpc tries + # for seconds before it daemonizes. The daemon must not take the + # lock with it, hence the 9>&-. And ifup must not be the last + # command here: ash execs that one in place of the subshell, the + # 9>&- then drops the lock for the whole ifup, and a pull during + # DHCP left usb0 recorded as configured every time. + ( + flock 9 + [ -e "/sys/class/net/$MDEV" ] || exit 0 + # A new interface: whatever ifupdown holds for the name + # is left over from the last one. + ifdown -f "$MDEV" + ifup "$MDEV" 9>&- || logger -t network "$MDEV: ifup failed" + ) 9>"$lock" /dev/null 2>&1 & + ;; + remove) + ( + flock 9 + ifdown -f "$MDEV" + ) 9>"$lock" >/dev/null 2>&1 + ;; +esac