This patch adds support for QMI to netifd. It enables netifd to access the
network functions of several 2G/3G/4G sticks. For usage of qmi, the device has
to support 2 functions:
-cdc-wdm interface for control
-wwan interface for data

Throuput should also be a lot better than through serial and pppd. To use this,
you should have a stick like a Huawei E392. It is configured through
/etc/config/network. Example:
config interface 'broadband'
        option ifname 'wwan0'
        option proto 'qmi'
        option pincode '1234'
        option apn 'web.vodafone.de'
        option username 'user'
        option password 'password'
        # if you want a sepearate LTE APN to be used, set
        option lte_apn_use '1'
        option lte_apn 'web.vodafone.de'
        option lte_username 'user'
        option lte_password 'password'

Stability is watched via a small watchdog, which does arp to check if modems 
still
responds. To be able to use username and password authentication, latest libqmi 
is
needed. Because I have no IPv6 enabled cellphone provider, this is not tested.

Any ideas are welcome!
Thanks to Aleksander Morgado for his great work!

Signed-off-by: André Valentin <avalen...@marcant.net>
---

diff -uNrp net/netifd-qmi/files/lib/netifd/dhcp-qmi.script 
net/netifd-qmi/files/lib/netifd/dhcp-qmi.script
--- net/netifd-qmi/files/lib/netifd/dhcp-qmi.script     1970-01-01 
01:00:00.000000000 +0100
+++ net/netifd-qmi/files/lib/netifd/dhcp-qmi.script     2013-01-08 
15:32:29.000000000 +0100
@@ -0,0 +1,50 @@
+#!/bin/sh
+[ -z "$1" ] && echo "Error: should be run by udhcpc" && exit 1
+
+. /lib/functions.sh
+. /lib/netifd/netifd-proto.sh
+
+set_classless_routes() {
+       local max=128
+       local type
+       while [ -n "$1" -a -n "$2" -a $max -gt 0 ]; do
+               proto_add_ipv4_route "${1%%/*}" "${1##*/}" "$2"
+               max=$(($max-1))
+               shift 2
+       done
+}
+
+setup_interface () {
+       proto_init_update "*" 1
+       subnet=255.255.255.255
+       proto_add_ipv4_address "$ip" "${subnet:-255.255.255.0}" 255.255.255.255 
"$router"
+       proto_add_ipv4_route 0.0.0.0 0 "$router"
+
+       # CIDR STATIC ROUTES (rfc3442)
+       [ -n "$staticroutes" ] && set_classless_routes $staticroutes
+       [ -n "$msstaticroutes" ] && set_classless_routes $msstaticroutes
+
+       for dns in $dns; do
+               proto_add_dns_server "$dns"
+       done
+       for domain in $domain; do
+               proto_add_dns_search "$domain"
+       done
+       proto_send_update "$INTERFACE"
+}
+
+deconfig_interface() {
+       proto_init_update "*" 0
+       proto_send_update "$INTERFACE"
+}
+
+case "$1" in
+       deconfig)
+               deconfig_interface
+       ;;
+       renew|bound)
+               setup_interface
+       ;;
+esac
+
+exit 0
diff -uNrp net/netifd-qmi/files/lib/netifd/proto/qmi.sh 
net/netifd-qmi/files/lib/netifd/proto/qmi.sh
--- net/netifd-qmi/files/lib/netifd/proto/qmi.sh        1970-01-01 
01:00:00.000000000 +0100
+++ net/netifd-qmi/files/lib/netifd/proto/qmi.sh        2013-02-28 
11:41:14.000000000 +0100
@@ -0,0 +1,307 @@
+#!/bin/sh
+#
+# 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, version 2.
+#
+# 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, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2012 Aleksander Morgado <aleksan...@gnu.org>
+# Copyright (C) 2012 André Valentin / MarcanT GmbH <avalen...@marcant.net>
+
+. /lib/functions.sh
+. ../netifd-proto.sh
+init_proto "$@"
+
+proto_qmi_init_config() {
+       proto_config_add_string "ipaddr"
+       proto_config_add_string "netmask"
+       proto_config_add_string "hostname"
+       proto_config_add_string "clientid"
+       proto_config_add_string "vendorid"
+       proto_config_add_boolean "broadcast"
+       proto_config_add_string "reqopts"
+       proto_config_add_string "apn"
+       proto_config_add_string "username"
+       proto_config_add_string "password"
+       proto_config_add_boolean "lte_apn_use"
+       proto_config_add_string "lte_apn"
+       proto_config_add_string "lte_username"
+       proto_config_add_string "lte_password"
+       proto_config_add_string "pincode"
+       proto_config_add_string "technology"
+       proto_config_add_string "auto"
+}
+
+proto_qmi_log() {
+       local level="$1"
+       local message="$2"
+       [ -z "$message" ] && {
+               logger -p "$1" -t "qmi[$$]"
+               return;
+       }
+       shift
+       logger -p "$level" -t "qmi[$$]" -- "$@"
+}
+
+proto_qmi_clear_state() {
+       local $config=$1
+       uci_revert_state network $config CID
+       uci_revert_state network $config PDH
+       uci_revert_state network $config QMIDEVICE
+}
+
+proto_qmi_start_network() {
+       local config=$1
+       local DEVICE=$2
+       local APN="$3"
+       local CID=$(uci_get_state network $config CID)
+       local PDH=$(uci_get_state network $config PDH)
+
+       if [ "x$CID" != "x" ]; then
+               USE_PREVIOUS_CID="--client-cid=$CID"
+       fi
+
+       if [ "x$PDH" != "x" ]; then
+               logger -p daemon.info  "error: cannot re-start network, PDH 
already exists"
+               return 3
+       fi
+
+       START_NETWORK_CMD="qmicli -d $DEVICE --wds-start-network=$APN 
$USE_PREVIOUS_CID --client-no-release-cid"
+       logger -p daemon.info  "Starting network with '$START_NETWORK_CMD'..."
+       START_NETWORK_OUT=`$START_NETWORK_CMD`
+
+       # Save the new CID if we didn't use any before
+       if [ "x$CID" = "x" ]; then
+               CID=`echo "$START_NETWORK_OUT" | grep CID | sed "s/'//g" | awk 
'BEGIN { FS = ": " } ; { print $2 }'`
+               if [ "x$CID" = "x" ]; then
+                       logger -p daemon.info  "error: network start failed, 
client not allocated"
+                       return 1
+               else
+                       uci_set_state network $config CID $CID
+               fi
+       fi
+
+       PDH=`echo "$START_NETWORK_OUT" | grep handle | sed "s/'//g" | awk 
'BEGIN { FS = ": " } ; { print $2 }'`
+       if [ "x$PDH" = "x" ]; then
+               proto_qmi_log daemon.info "error: network start failed, no 
packet data handle"
+               # Cleanup the client
+               qmicli -d "$DEVICE" --wds-noop --client-cid="$CID"
+               uci_revert_state network $config CID
+               uci_revert_state network $config PDH
+               uci_revert_state network $config QMIDEVICE
+               CID=""
+               PDH=""
+               return 2
+       else
+               uci_set_state network $config PDH "$PDH"
+       fi
+       uci_set_state network $config QMIDEVICE "$DEVICE"
+       proto_qmi_log daemon.info "Network started successfully: CID: $CID, 
PDH=$PDH, DEVICE=$DEVICE"
+}
+
+proto_qmi_stop_network () {
+       local config=$1
+       local CID=$(uci_get_state network $config CID)
+       local PDH=$(uci_get_state network $config PDH)
+       local DEVICE=$(uci_get_state network $config QMIDEVICE)
+
+       if [ "x$CID" = "x" ]; then
+               proto_qmi_log daemon.info "Network already stopped"
+       elif [ "x$PDH" = "x" ]; then
+               proto_qmi_log daemon.info "Network already stopped; need to 
cleanup CID $CID"
+               # Cleanup the client
+               qmicli -d "$DEVICE" --wds-noop --client-cid="$CID"
+       else
+               STOP_NETWORK_CMD="qmicli -d $DEVICE --wds-stop-network=$PDH 
--client-cid=$CID"
+               proto_qmi_log daemon.info "Stopping network with 
'$STOP_NETWORK_CMD'..."
+               STOP_NETWORK_OUT=`$STOP_NETWORK_CMD`
+               proto_qmi_log daemon.info "Network stopped successfully"
+       fi
+       uci_revert_state network $config CID
+       uci_revert_state network $config PDH
+       uci_revert_state network $config QMIDEVICE
+}
+
+proto_qmi_packet_service_status () {
+       local config=$1
+       local CID=$(uci_get_state network $config CID)
+       local PDH=$(uci_get_state network $config PDH)
+       local DEVICE=$(uci_get_state network $config QMIDEVICE)
+
+       if [ "x$CID" != "x" ]; then
+               USE_PREVIOUS_CID="--client-cid=$CID --client-no-release-cid"
+       fi
+
+       STATUS_CMD="qmicli -d $DEVICE --wds-get-packet-service-status 
$USE_PREVIOUS_CID"
+
+       STATUS_OUT=`$STATUS_CMD`
+
+       CONN=`echo "$STATUS_OUT" | grep "Connection status" | sed "s/'//g" | 
awk 'BEGIN { FS = ": " } ; { print $2 }'`
+       if [ "x$CONN" = "x" ]; then     
+               proto_qmi_log daemon.info "error: couldn't get packet service 
status"
+               return 2
+       else
+               if [ "x$CONN" != "xconnected" ]; then
+                       proto_qmi_log daemon.debug "Status: $CONN"
+                       return 64
+               fi
+       fi
+}
+
+proto_qmi_setup() {
+       local config="$1"
+       local iface="$2"
+       local ipaddr hostname clientid vendorid broadcast reqopts apn username 
password pincode auto lte_apn_use lte_apn lte_username lte_password
+       json_get_vars ipaddr hostname clientid vendorid broadcast reqopts apn 
username password pincode auto data lte_apn_use lte_apn lte_username 
lte_password
+
+       # Load technology list
+       config_load network
+       config_get technology $config technology
+
+       # Setup APN config
+       apn_standard="${apn}"
+       [ -n "${username}" ] && {
+               apn_standard="${apn_standard},both,${username}"
+       }
+       [ -n "${password}" ] && {
+               apn_standard="${apn_standard},${password}"
+       }
+       apn_lte="${lte_apn}"
+       [ -n "${lte_username}" ] && {
+               apn_lte="${apn_lte},both,${lte_username}"
+       }
+       [ -n "${password}" ] && {
+               apn_lte="${apn_lte},${lte_password}"
+       }
+       
+       # Reset auto state if forced by technology selection
+       uci_revert_state network $config auto
+       
+       local CDCDEV
+       CDCDEV=/dev/$(basename $(ls 
/sys/class/net/${iface}/device/usbmisc/cdc-wdm* -d)) || {
+               CDCDEV="$device"
+               proto_qmi_log daemon.err "Control device not found, using 
network.${config}.device: $CDCDEV"
+               return 1
+       }
+       proto_qmi_log daemon.info "Wan Device: ${iface}, Control CDC: 
${CDCDEV}, APN: $apn_standard, LTE APN: $apn_lte, Pincode: $pincode"
+       if [ -z "$CDCDEV" ]; then
+               proto_qmi_log daemon.err "Device $CDCDEV is empty"
+       fi
+       while ! [ -c "$CDCDEV" ]; do
+               proto_qmi_log daemon.debug "Waiting for device creation: 
${CDCDEV}"
+               sleep 2
+       done
+       # Just in case there is still context data
+       proto_qmi_stop_network ${config}
+       
+       # Check PIN
+       [ -n "$pincode" ] && {
+               set -o pipefail
+               if ! qmicli -d $CDCDEV "--dms-uim-verify-pin=PIN,${pincode}" 
2>&1 | proto_qmi_log daemon.info; then
+                       qmicli -d $CDCDEV --dms-uim-get-pin-status | 
proto_qmi_log daemon.info
+                       proto_qmi_log daemon.info "PIN Verification failed, 
shutting down and block restart."
+                       proto_notify_error "$config" PIN_FAILED
+                       proto_block_restart "$interface"
+                       return 1
+               fi
+       }
+       # Print info about system selection for debugging purpose
+       qmicli -d $CDCDEV --nas-get-system-selection-preference 2>&1 | 
proto_qmi_log daemon.debug
+
+       # Wait for registration
+       while ! qmicli -d $CDCDEV --nas-get-serving-system|grep 'Registration 
state'|grep "'registered'" > /dev/null; do
+               sleep 1;
+               proto_qmi_log daemon.info "Waiting for registration"
+       done
+
+       # Print current network info
+       qmicli -d $CDCDEV --nas-get-serving-system 2>&1 | proto_qmi_log 
daemon.debug
+
+       # Select APN    
+       if qmicli -d $CDCDEV --nas-get-serving-system | grep -q "'lte'" > 
/dev/null && [ "${lte_apn_use}" = "1" ]; then
+               current_apn="$apn_lte"
+       else
+               current_apn="$apn_standard"
+       fi
+
+       # Try to start network  
+       set -o pipefail
+       while ! proto_qmi_start_network ${config} $CDCDEV "${current_apn}" 2>&1 
| proto_qmi_log daemon.info; do
+               sleep 5
+       done
+
+       # Show status and start watchdog
+       qmicli -d $CDCDEV --nas-get-serving-system 2>&1 | proto_qmi_log 
daemon.debug
+       (
+               set -o pipefail
+               let counter=0
+               while sleep 20; do
+                       proto_qmi_packet_service_status ${config}
+                       STATUS=$?
+                       [ "$STATUS" -gt 0 ] && {
+                               proto_qmi_log daemon.err "QMI Status shows 
error ${STATUS}, shutting down ${config}, control device $CDCDEV"
+                               (ifdown $config; sleep 30; ifup $config) 
</dev/null > /dev/null 2>&1 &
+                               exit 1
+                       }
+                       if ! router=$(ip neigh show dev ${iface} | cut -d ' ' 
-f1 ); then
+                               proto_qmi_log daemon.info "Neighbor not found"
+                               continue
+                       fi
+                       if ! arping -q -c1 -w 5 -I ${iface} $(ip neigh show dev 
${iface}|cut -d' ' -f1); then
+                               let counter=${counter}+1
+                               proto_qmi_log daemon.err "Arping gateway timed 
out, Error counter: $counter"
+                       fi
+                       [ "$counter" -gt 5 ] && {
+                               proto_qmi_log daemon.err "Error counter to 
high: $counter, shutting down ${config}, control device ${CDCDEV}"
+                               (ifdown $config; sleep 5; sleep 30; ifup 
$config) </dev/null > /dev/null 2>&1 &
+                               exit 1
+                       }
+               done
+       ) </dev/null > /dev/null 2>&1 &
+       local watchdog_pid=$!
+       echo $watchdog_pid > /var/run/qmi-watchdog-${config}.pid
+       proto_qmi_log daemon.info "Started watchdog pid $watchdog_pid"
+
+       local opt dhcpopts
+       for opt in $reqopts; do
+               append dhcpopts "-O $opt"
+       done
+
+       [ "$broadcast" = 1 ] && broadcast="-B" || broadcast=
+       [ -n "$clientid" ] && clientid="-x 0x3d:${clientid//:/}" || 
clientid="-C"
+
+       proto_export "INTERFACE=$config"
+       proto_run_command "$config" udhcpc \
+               -p /var/run/udhcpc-$iface.pid \
+               -s /lib/netifd/dhcp-qmi.script \
+               -f -t 0 -i "$iface" \
+               -x lease:60 \
+               ${ipaddr:+-r $ipaddr} \
+               ${hostname:+-H $hostname} \
+               ${vendorid:+-V $vendorid} \
+               $clientid $broadcast $dhcpopts
+}
+
+proto_qmi_teardown() {
+       local interface="$1"
+       local iface="$2"
+       local CID=$(uci_get_state network $interface CID)
+       local DEVICE=$(uci_get_state network $interface QMIDEVICE)
+       [ -e /var/run/qmi-watchdog-${interface}.pid ] && {
+               kill $(cat /var/run/qmi-watchdog-${interface}.pid)
+               rm /var/run/qmi-watchdog-${interface}.pid
+       }
+       proto_qmi_stop_network ${interface}
+       proto_kill_command "$interface"
+       echo "$interface done"
+}
+
+add_protocol qmi
diff -uNrp net/netifd-qmi/Makefile net/netifd-qmi/Makefile
--- net/netifd-qmi/Makefile     1970-01-01 01:00:00.000000000 +0100
+++ net/netifd-qmi/Makefile     2013-02-28 11:35:01.000000000 +0100
@@ -0,0 +1,39 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=netifd-qmi
+PKG_VERSION:=1.0
+PKG_RELEASE:=1
+
+PKG_MAINTAINER:=André Valentin <avalen...@marcant.net>
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/netifd-qmi
+  SECTION:=utils
+  CATEGORY:=Network
+  DEPENDS:=+libqmi +iputils-arping
+  TITLE:=QMI support for netifd
+endef
+
+define Package/netifd-qmi/description
+  Helper scripts to enable netifd to manage qmi interfaces
+endef
+
+define Build/Prepare
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/netifd-qmi/install
+       $(INSTALL_DIR) $(1)/lib
+       $(INSTALL_DIR) $(1)/lib/netifd
+       $(INSTALL_DIR) $(1)/lib/netifd/proto
+       $(INSTALL_BIN) ./files/lib/netifd/dhcp-qmi.script $(1)/lib/netifd/
+       $(INSTALL_BIN) ./files/lib/netifd/proto/qmi.sh $(1)/lib/netifd/proto/
+endef
+
+$(eval $(call BuildPackage,netifd-qmi))
_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to